Railway Operation Simulator  v2.9.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */{
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
229  {
231  }
232  if(FrontCodePtr == 0)
233  {
234  throw Exception("Error in attempting to delete FrontCodePtr");
235  }
236  delete FrontCodePtr;
237  FrontCodePtr = 0;
238  for(int x = 0; x < 4; x++)
239  {
240  if(BackgroundPtr[x] == 0)
241  {
242  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
243  }
244  delete BackgroundPtr[x];
245  BackgroundPtr[x] = 0;
246  }
247  for(int x = 0; x < 4; x++)
248  {
249  if(HeadCodeGrPtr[x] == 0)
250  {
251  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
252  }
253  delete HeadCodeGrPtr[x];
254  HeadCodeGrPtr[x] = 0;
255  }
256  Utilities->CallLogPop(649);
257 }
258 
259 // ---------------------------------------------------------------------------
260 
262 /*
263  Plots the train starting position on screen. Note that the check for starting on straight points &
264  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
265  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
266  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
267  Set the headcode graphics pointers from the headcode text, then check whether starting at a
268  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
269  for the continuation element. Otherwise set Lead and Mid values,
270 
271  and Lead element value unless
272  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
273  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
274  then check if a train on either Mid or Lag and if so give a warning message and return false so
275  that the calling function can delete the train. Plot the Mid element train values then do similarly
276  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
277  the train. Finally set the Plotted flag and return true.
278 */{
279  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
280  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
281 
283  // PlotStartTime = TrainController->TTClockTime;
284  FirstHalfMove = true;
285 
286  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
287  // 'claim' it for this train to prevent any other waiting trains trying to enter
289  {
290  LagElement = -1; // not to be plotted
291  LagExitPos = 0; // not to be plotted
292  LagEntryPos = 0; // not to be plotted
293  MidElement = -1; // not to be plotted
294  MidExitPos = 0; // not to be plotted
295  MidEntryPos = 0; // not to be plotted
297  LeadExitPos = 1; // will be 1 for continuation entry
298  LeadEntryPos = 0;
299 
301  MaxExitSpeed = StartSpeed; // initial value
303  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
304  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
305  if(EntrySpeed > SpeedLimit)
306  {
307  EntrySpeed = SpeedLimit;
308  }
310  {
312  }
314  // LeadElement is the element to be entered
315 
316  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
317  // can achieve ExitSpeedFull at the half braking rate.
319  {
320  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
321  if(TempEntrySpeed < EntrySpeed)
322  {
323  EntrySpeed = TempEntrySpeed;
325  }
326  }
327  Straddle = MidLag; // only for starting on a continuation
329  // no need to stop gap flashing if start on continuation
330  }
331  else // not starting at a continuation
332  {
333  LagElement = -1;
334  LagEntryPos = 0;
335  LagExitPos = 0;
342 
344  MaxExitSpeed = StartSpeed; // initial value
346  bool TempDerail = false; // dummy
347  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
349  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
350  {
351  StoppedWithoutPower = true;
352  }
353  // facing buffers check - ignore starting speed if start facing buffers
354  StoppedAtBuffers = false;
355  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
358  {
359  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
360  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
361  EntrySpeed = 0;
362  ExitSpeedHalf = 0;
363  ExitSpeedFull = 0;
364  MaxExitSpeed = 0;
365  // SetTrainMovementValues not called so set this here
366  BrakeRate = 0;
369  StoppedAtSignal = false;
370  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
371  // signal check is an 'else'
372  if(!StoppedAtLocation)
373  {
374  StoppedAtBuffers = true; // stopped at location takes precedence
375  }
376  }
377 
378  // facing continuation check - don't allow to stop even if no power
380  {
381  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
382  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
386  BrakeRate = 0;
387  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
388  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
389  }
390 
391  // Signal check
392  else if((NextElementPosition > -1) && (NextEntryPos > -1))
393  // condition check added as precaution after SloughIECC error reported by James U
394  {
395  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
396  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
397  {
398  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
399  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
400  EntrySpeed = 0;
401  ExitSpeedHalf = 0;
402  ExitSpeedFull = 0;
403  MaxExitSpeed = 0;
404  BrakeRate = 0;
407  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
408  {
409  StoppedAtSignal = true;
411  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
412  }
414  {
415  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
416  // forwards, but don't change the background colour so still shows as stopped at location
417  StoppedAtSignal = true;
418  }
419  }
420  else
421  {
422  StoppedAtSignal = false;
423  if(NextEntryPos > 1)
424  {
425  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
426  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
427  }
428  else
429  {
430  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
431  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
432  }
433  if(EntrySpeed > SpeedLimit)
434  {
435  EntrySpeed = SpeedLimit;
436  }
438  {
440  }
442  TDateTime TestTime = TrainController->TTClockTime; // test
443  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
444  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
445  // NextElement is the element to be entered
446 
447  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
448  // can achieve ExitSpeedFull at the half braking rate.
450  {
451  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
452  // half braking
453  if(TempEntrySpeed < EntrySpeed)
454  {
455  EntrySpeed = TempEntrySpeed;
456  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
457  }
458  }
459  }
460  }
462  {
463  throw Exception("Error, LeadElement Exit Connection is NotSet");
464  }
465  }
466  if(MidElement > -1) // will be -1 if start on continuation
467  {
468  Straddle = LeadMid;
472  {
473  for(int x = 0; x < 4; x++)
474  {
475  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
476  }
477  }
478  else
479  {
480  for(int x = 0; x < 4; x++)
481  {
483  }
484  }
485  if(TrainMode == Timetable)
486  {
488  }
489  else
490  {
492  }
494  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
495 
498 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
499  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
500  {
501  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
502  Utilities->CallLogPop(651);
503  return false;
504  }
505 */
510  PlotTrainGraphic(8, 0, Display);
511  PlotTrainGraphic(9, 1, Display);
512 
515 
516  // pick up background bitmaps [2] & [3]
517 
520 
521  PlotElement[2] = MidElement;
523  PlotElement[3] = MidElement;
525  PlotTrainGraphic(10, 2, Display);
526  PlotTrainGraphic(11, 3, Display);
527  // Plotted = true; set in PlotTrainGraphic
528  }
529  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
530  Utilities->CallLogPop(652);
531 }
532 
533 // ---------------------------------------------------------------------------
534 void TTrain::UnplotTrain(int Caller)
535 {
536  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
537  if(!Plotted)
538  {
539  return;
540  }
541  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
542 
543  if(Straddle == MidLag)
544  {
545  if(MidElement > -1)
546  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(LagElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  }
562  else if(Straddle == LeadMidLag)
563  {
564  if(LeadElement > -1)
565  {
568  // to force plot of locked route marker, needed once only for the element
569  }
570  if(MidElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(LagElement > -1)
579  {
582  // to force plot of locked route marker, needed once only for the element
583  }
584  }
585  else if(Straddle == LeadMid)
586  {
587  if(LeadElement > -1)
588  {
593  // to force plot of locked route marker, needed once only for the element
594  }
595  if(MidElement > -1)
596  {
601  // to force plot of locked route marker, needed once only for the element
602  }
603  }
604  if(LeadElement > -1)
605  {
607  }
608  if(MidElement > -1)
609  {
611  }
612  if(LagElement > -1)
613  {
615  }
616  Plotted = false;
618  Display->Update();
619  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
620  // resurrected when Update() dropped from PlotOutput etc
621  Utilities->CallLogPop(653);
622 }
623 
624 // ----------------------------------------------------------------------------
625 
626 void TTrain::UpdateTrain(int Caller)
627 /*
628  Note: Some changes made since comments written
629 
630  Brief:
631  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
632  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
633  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
634  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
635  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
636  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
637  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
638  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
639  changed to MidLag within the function and all elements moved down one, old Mid becomes
640  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
641  incremented to reflect the position the train now occupies.
642 
643  Detail:
644  Set TrainFailurePending if all conditions met
645  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
646  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
647  and return.
648  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
649  If there's a LagElement (there will be but include check for good practice - next
650  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
651  train fully on offending point - Derail set and DerailPanding reset, train background
652  colour changed (note that BackgroundColour is a property of the train itself) then return.
653  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
654  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
655  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
656  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
657  if LeadElement is a fouled trailing point.
658  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
659  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
660  replotting the last background segment and checking whether the element is a bridge or crossover with the other
661  track in a route, in which case the route colour is replotted.
662  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
663  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
664  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
665  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
666  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
667  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
668  train can be deleted by the calling function, and the function returns.
669  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
670  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
671  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
672  basic red aspect.
673 
674  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
675  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
676  regardless of direction, and with the correct front code colour.
677 
678  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
679  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
680  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
681  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
682  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
683  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
684  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
685 
686  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
687  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
688  changed similarly. The function then returns.
689 
690  If Crashed is not set then Straddle is incremented and the function returns.
691 */
692 
693 {
694  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
695  UpdateCounter++;
696  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
697  if(UpdateCounter >= 100)
698  {
699  UpdateCounter = 0;
700  }
701  int RandRange = (TrainController->MTBFHours * 3600) / 53;
702 
703  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
704  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
705  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
706  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
707  // RandomFailureCounter value is fixed for a full cycle of train updates so this
708  // makes sure there's no bunching of failures as there is for a fixed comparison number
709  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
710  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
711  {
712  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
713  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
714  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
715  // don't fail if:
716  // (a) on a continuation (entering or leaving);
717  // (b) already failed;
718  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
719  // (d) train terminated;
720  // (e) crashed or derailed; or
721  // (f) under signaller control and stopped.
722  {
723  if((random(RandRange)) == 0)
724  // max value for RandRange is over 2x10^9
725  {
726  // here if failure due
727  TrainFailurePending = true;
728  // fail when PlotElements set to proper Lead, Mid & Elements
729  }
730  }
731  }
732 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
733  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
734  {
735  StoppedWithoutPower = true;
736  }
737 */
738  int LockedVectorNumber;
739  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
740  // default values - these needed for route checker below
741  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
742 
744  {
746  }
747  if(Crashed || Derailed)
748  {
750  {
751  PlotTrain(7, Display);
752  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
753  Display->Update();
754  }
755  OpTimeToAct = 0.0;
756  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
757  Utilities->CallLogPop(1017);
758  return; // no further action, user has to remove or work around
759  }
761  {
763  }
765  {
767  }
769  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
770  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
771  // to move & then stop again at the same station
772  {
773  TimeTimeLocArrived = false;
774  }
775  if(!Stopped() && !SPADFlag && !TrainFailed)
776  {
778  }
779  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
780  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
781 /* old version where force a stop at buffers regardless of speed
782  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
783  else StoppedAtBuffers = false;
784 */
785 
786  // new version where crash if run into buffers
787  if(!Crashed)
788  {
789  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
790  {
791  if(ExitSpeedFull > 1)
792  {
793  Crashed = true;
797  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
798  // no need for missed action logs - will be sent when train removed
799  StoppedAtBuffers = false;
800  }
802  // stopped at location & stopped without power take precedence
803  {
804  StoppedAtBuffers = true;
805  }
806  else
807  {
808  StoppedAtBuffers = false;
809  }
810  }
811  else
812  {
813  StoppedAtBuffers = false;
814  }
815  }
816  else
817  {
818  StoppedAtBuffers = false;
819  }
820  // if crashed don't want stopped at buffers set
821 
822  // also crash if run into a level crossing that is changing or has barriers up
823  if(!Crashed)
824  {
825  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
826  {
827  int H = Track->TrackElementAt(873, LeadElement).HLoc;
828  int V = Track->TrackElementAt(874, LeadElement).VLoc;
829  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
830  {
831  Crashed = true;
835  // no need for missed action logs - will be sent when train removed
836  }
837  }
838  }
840  {
842  }
843  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
844  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
845  {
846  HoldAtLocationInTTMode = true;
847  }
848  else if(TrainMode == Timetable)
849  {
850  HoldAtLocationInTTMode = false;
851  }
852  // in Signaller mode HoldAtLocationInTTMode not changed
853 
854  // check if departure pending & set times unless already set
855  if(TrainMode == Timetable)
856  {
858  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
859  {
860  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
861  {
863  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
864  {
865  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
866  }
867  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
868  DepartureTimeSet = true;
869  }
870  }
871  }
873  {
874  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
875  // calculate every 1 sec (in real time, not timetable time) for all trains
876  }
877  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
878  if(TrainMode == Timetable)
879  {
880  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
881  {
882  RemainHereLogNotSent = true;
883  }
885  {
886  // ignore TimeLoc & TTLoc departures
887  // Action logs given in functions
889  LastActionTime + TDateTime(30.0 / 86400)))
890  {
891  if(ActionVectorEntryPtr->Command == "fsp")
892  {
893  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
894  FrontTrainSplit(0);
895  if(TrainFailurePending) // ok, stopped so PlotElements set
896  {
897  TrainHasFailed(0);
898  }
899  Utilities->CallLogPop(2041);
900  return;
901  }
902  else if(ActionVectorEntryPtr->Command == "rsp")
903  {
904  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
905  RearTrainSplit(0);
906  if(TrainFailurePending) // ok, stopped so PlotElements set
907  {
908  TrainHasFailed(1);
909  }
910  Utilities->CallLogPop(2042);
911  return;
912  }
913  else if(ActionVectorEntryPtr->Command == "Fjo")
914  {
915  FinishJoin(0);
916  }
917  else if(ActionVectorEntryPtr->Command == "jbo")
918  {
919  JoinedBy(0);
920  }
921  else if(ActionVectorEntryPtr->Command == "cdt")
922  {
924  }
925  else if(ActionVectorEntryPtr->Command == "Fns")
926  {
927  NewTrainService(0);
928  }
929  else if(ActionVectorEntryPtr->Command == "Frh")
930  {
931  RemainHere(0);
932  }
933  else if(ActionVectorEntryPtr->Command == "Fer")
934  {
935  TimetableFinished = true;
936  }
937  // other aspects of 'Fer' dealt with in TTrain::Update()
938  else if(ActionVectorEntryPtr->Command == "F-nshs")
939  {
941  }
942  else if(ActionVectorEntryPtr->Command == "Frh-sh")
943  {
945  }
946  else if(ActionVectorEntryPtr->Command == "Fns-sh")
947  {
949  }
950 /*
951  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
952  shuttle headcode (no train creation)
953  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
954  remain here
955  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
956  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
957 */
958  }
959  }
960  else
961  {
963  {
965  }
966  }
967  }
968  if(TrainMode == Timetable)
969  {
970  if(StoppedAtBuffers)
971  {
972  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
973  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
974  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
975  if(BufferLocation == "")
976  {
978  }
979  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
980  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
981  {
985  {
987  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
988  // Drop missed actions so user can still use sig mode to get back on track
990  }
991  if(TrainFailurePending) // ok, stopped so PlotElements set
992  {
994  TrainHasFailed(2);
995  }
996  Utilities->CallLogPop(1020);
997  return;
998  }
999  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
1000  ReleaseTime))
1001  {
1004  {
1007  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1008  // Drop missed actions so user can still use sig mode to get back on track
1010  }
1011  if(TrainFailurePending) // ok, stopped so PlotElements set
1012  {
1014  TrainHasFailed(3);
1015  }
1016  Utilities->CallLogPop(1397);
1017  return;
1018  }
1019  }
1020  else
1021  {
1023  }
1024  }
1025  else
1026  {
1028  }
1029  if(TrainMode == Timetable)
1030  {
1032  {
1034  }
1036  {
1038  }
1039  }
1040  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1041  // restart after stopped for train in front
1042  int NextElementPosition, NextEntryPos;
1043 
1044  if(LeadElement > -1) // if an exit continuation then not set
1045  {
1046  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1047  {
1049  }
1050  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1051  {
1052  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1053  {
1054  LeadExitPos = 1;
1055  }
1056  else
1057  {
1058  LeadExitPos = 3;
1059  }
1060  }
1061  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1062  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1063  }
1064  else
1065  {
1066  NextElementPosition = -1;
1067  NextEntryPos = -1;
1068  }
1069  if((NextElementPosition > -1) && (NextEntryPos > -1))
1070  // may be buffers or continuation so need this check
1071  {
1072 /*
1073  Check whether calling-on conditions met:-
1074  a) approaching train has stopped at a signal but not at a location;
1075  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1076  change of direction (cdt), remaining here (Frh), or under signaller control);
1077  c) at least 1 platform available for the approaching train;
1078  d) points (if any) set for direct route into platform;
1079  e) approaching train is to stop at station;
1080  f) no more facing signals between train and platform;
1081  g) [dropped g]
1082  h) train in front preventing route being set far enough to release stop signal;
1083  i) train in front not exiting at continuation;
1084  j) signal must be within 4km of the stop platform;
1085  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1086  l) no existing route conflicts with the route into the platform.
1087  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1088  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1089 */
1090  if(TrainMode == Timetable)
1091  {
1092  if(CallingOnAllowed(0))
1093  {
1094  CallingOnFlag = true;
1095  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1096  }
1097  else
1098  {
1099  if(CallingOnFlag)
1100  {
1102  }
1103  CallingOnFlag = false;
1104  }
1105  }
1106  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1107  {
1108  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1109  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1110  // sets StoppedAtSignal again & train doesn't move
1111  StoppedAtSignal = false;
1112  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1113  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1114  // LeadMidLag and front of train was on LeadElement (after the current move)
1116  EntrySpeed = 0;
1118  FirstHalfMove = true;
1119  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1120  // NextElement is the element to be entered
1121  }
1123  {
1124  if(ClearToNextSignal(0))
1125  {
1126  StoppedForTrainInFront = false;
1127  BeingCalledOn = false;
1128  EntrySpeed = 0;
1130  FirstHalfMove = true;
1131  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1132  }
1133  else
1134  {
1135  if(TrainFailurePending) // ok, stopped so PlotElements set
1136  {
1137  TrainHasFailed(4);
1138  }
1139  Utilities->CallLogPop(1097);
1140  return;
1141  }
1142  }
1143  }
1144  if((Straddle == MidLag) && (LeadElement != -1))
1145  // later check only for Straddle == LeadMid, so need this check here for initial train start
1146  {
1148  }
1149 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1150  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1151  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1152  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1153  which could be when start as Snt.
1154  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1155  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1156  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1157  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1158  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1159  reached.
1160  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1161  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1162  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1163  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1164  sending a message to the performancelog.
1165 */
1166 
1167  if(TrainMode == Timetable)
1168  {
1170  {
1171  if(BeingCalledOn)
1172  {
1173  StoppedForTrainInFront = true;
1174  }
1176  {
1178  }
1180  {
1181  // value updated at every scheduled departure & arrival
1183  AnsiString StationName;
1185  {
1187  }
1189  {
1191  }
1192  else
1193  {
1194  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1195  }
1196  EntrySpeed = 0;
1200  FirstHalfMove = true;
1201  StoppedAtLocation = false;
1202 
1203  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1204  {
1205  StoppedWithoutPower = true;
1206  }
1207  if((NextElementPosition > -1) && (NextEntryPos > -1))
1208  // condition check added for SloughIECC error reported by James U
1209  {
1210  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1211  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1212  {
1213  StoppedAtSignal = true;
1214  if(!StoppedWithoutPower)
1215  // if stopped without power just keep existing background colour
1216  {
1218  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1219  }
1220  }
1221  }
1223  {
1224  TimeTimeLocArrived = false;
1225  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1226  // no warning for TimeTimeLoc departure
1227  }
1228  else
1229  {
1231  }
1232  DepartureTimeSet = false;
1233  // no need to set LastActionTime for a departure
1235  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1236  // note
1237 /*
1238  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1239  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1240  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1241  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1242  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1243  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1244  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1245 */
1247  {
1248  StoppedAtBuffers = true;
1249  }
1250  else if(!StoppedWithoutPower)
1251  // if buffers or no power, don't set values
1252  {
1254  {
1255  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1256  // NextElement is the element to be entered
1257  }
1258  else
1259  {
1261  // use LeadElement for an exit continuation
1262  }
1263  }
1264  }
1265  }
1266  }
1267  if(Straddle == LeadMidLag)
1268  {
1270  {
1271  Utilities->CallLogPop(654);
1272  return;
1273  }
1274  }
1275  else
1276  {
1278  {
1279  Utilities->CallLogPop(655);
1280  return;
1281  }
1282  }
1283  if((LeadElement > -1) && (MidElement > -1))
1284  {
1286  {
1287  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1288  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1289  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1290  SignallerStoppingFlag = false;
1291  StepForwardFlag = false;
1292  }
1293  }
1294  if(Stopped())
1295  // this is what prevents another movement if the train is stopped
1296  {
1297  if(TrainFailurePending) // ok, stopped so PlotElements set
1298  {
1299  TrainHasFailed(5);
1300  }
1301  BrakeRate = 0;
1302  Utilities->CallLogPop(656);
1303  return;
1304  }
1305  // here when ready for next move
1306 
1307  // check for train in front & if so stop at next access (when train fully on element next to train)
1308  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1309  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1310  // variable TrainInFrontInSignallerModeFlag
1311  {
1312  if(LeadElement > -1)
1313  {
1314  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1315  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1316  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1317  // true if another train on NextEntryPos track whether bridge or not
1318  {
1319  StoppedForTrainInFront = true;
1320  }
1321  else
1322  {
1323  StoppedForTrainInFront = false;
1324  }
1325  }
1326  }
1327  if((Straddle == LeadMid) && SPADFlag)
1328  // give message + plot background when ready to move half past the signal
1329  {
1330  if(NextElementPosition > -1)
1331  {
1332  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1333  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1334  {
1335  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1337  // if goes past 2 signals then give message twice
1339  }
1340  }
1341  }
1342  if(Straddle == LeadMidLag)
1343  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1344  {
1345  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1346  if(SPADFlag)
1347  {
1348  if(ExitSpeedFull == 0)
1349  {
1350  StoppedAfterSPAD = true;
1351  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1352  }
1353  }
1355  {
1356  if(ExitSpeedFull == 0)
1357  {
1358  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1359  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1360  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1361  // is sent at the right time and once only.
1362  SignallerStopped = true;
1363  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1364  StepForwardFlag = false;
1365  SignallerStoppingFlag = false;
1366  TTrackElement TE;
1367  AnsiString Loc = "";
1368  bool LocNamed = false;
1369  if(LeadElement > -1)
1370  {
1371  TE = Track->TrackElementAt(782, LeadElement);
1372  if(TE.ActiveTrackElementName != "")
1373  {
1374  Loc = TE.ActiveTrackElementName;
1375  LocNamed = true;
1376  }
1377  else
1378  {
1379  Loc = "track element " + TE.ElementID;
1380  }
1381  }
1382  if((MidElement > -1) && !LocNamed)
1383  {
1384  TE = Track->TrackElementAt(783, MidElement);
1385  if(TE.ActiveTrackElementName != "")
1386  {
1387  Loc = TE.ActiveTrackElementName;
1388  LocNamed = true;
1389  }
1390  else if(Loc == "")
1391  {
1392  Loc = "track element " + TE.ElementID;
1393  }
1394  }
1395  if(Loc == "")
1396  {
1397  Loc = "outside railway";
1398  // must have stopped after left at a continuation (because both lead & mid == -1)
1399  }
1400  else
1401  {
1402  Loc = "at " + Loc;
1403  }
1404  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1405  }
1406  }
1407  if(LeadElement > -1) // if an exit continuation then not set
1408  {
1409  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1410  {
1412  }
1413  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1414  {
1415  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1416  {
1417  LeadExitPos = 1;
1418  }
1419  else
1420  {
1421  LeadExitPos = 3;
1422  }
1423  }
1424  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1425  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1426  }
1427  else
1428  {
1429  NextElementPosition = -1;
1430  NextEntryPos = -1;
1431  }
1434  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1435 
1436  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1437  {
1438  StoppedWithoutPower = true;
1439  }
1440  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1441  // may be buffers or continuation. SPADFlag added at v2.1.0
1442  // so don't override the SPAD colour & don't set StoppedAtSignal
1443  {
1444  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1445  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1446  {
1447  StoppedAtSignal = true;
1448  if(!StoppedWithoutPower)
1449  // leave background as is if no power, but set StoppedAtSignal
1450  {
1452  }
1453  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1454  }
1455  }
1456  if(!Stopped())
1457  {
1458  if((NextElementPosition > -1) && (NextEntryPos > -1))
1459  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1460  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1461  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1462  // function for fuller explanation
1463  {
1464  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1465  // NextElement is the element to be entered
1466  }
1467  // follow the continuation exits:-
1468  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1469  {
1471  // Use LeadElement for calcs if lead is a continuation
1472  }
1473  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1474  {
1476  // Use MidElement for calcs if mid is a continuation
1477  }
1478  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1479  {
1481  // Use LagElement for calcs if lag is a continuation
1482  }
1483  }
1484  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1485  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1486  // Trains may not be in a route
1487  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1488  {
1489  // NB if LeadElement == -1 then the above test returns false
1490  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1491  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1492  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1493  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1494  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1495  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1496  FirstPair.second).GetELink() == TempELink))
1497  {
1498  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1499  }
1500  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1501  SecondPair.second).GetELink() == TempELink))
1502  {
1503  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1504  }
1505  }
1506  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1507  // Trains may not be in a route
1508  {
1509  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1510  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1511  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1512  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1513  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1514  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1515  FirstPair.second).GetELink() == TempELink))
1516  {
1517  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1518  }
1519  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1520  SecondPair.second).GetELink() == TempELink))
1521  {
1522  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1523  }
1524  }
1525  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1526  // Trains may not be in a route
1527  {
1528  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1529  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1530  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1531  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1532  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1533  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1534  FirstPair.second).GetELink() == TempELink))
1535  {
1536  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1537  }
1538  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1539  SecondPair.second).GetELink() == TempELink))
1540  {
1541  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1542  }
1543  AllRoutes->CheckMapAndRoutes(8); // test
1544  }
1545  if(LagElement > -1)
1546  // not entering at a continuation so can deal with train leaving the lag element
1547  {
1549  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1550  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1551  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1552 
1553  TPrefDirElement PrefDirElement;
1554  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1555  // as this is a 16x16 graphic
1557  {
1559  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1560  }
1561  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1562  {
1563  int RouteNumber;
1564  TrainGone = true;
1565  // flag to indicate train to be deleted - outside this function
1567  {
1568  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1569  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1570  // calc distance from & inc last signal to exit
1571  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1572  int NewLastElement = 0, NewLastExitPos = 0;
1573  // need above because can't change LastElement & LastExitPos until both new values obtained
1574  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1575  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1576  LastElement).TrackType != Points))
1577  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1578  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1579  // leave CumDistance as it was in these circumstances.
1580  {
1581  if(LastExitPos < 2)
1582  {
1583  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1584  }
1585  else
1586  {
1587  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1588  }
1589  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1590  if(NewLastElement == -1)
1591  // this will catch buffers or any other connection failure
1592  {
1593  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1594  }
1595  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1596  if(NewLastExitPos == -1)
1597  {
1598  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1599  }
1600  LastElement = NewLastElement;
1601  LastExitPos = NewLastExitPos;
1602  }
1603  // if at signal add this in too
1604  if(CumDistance < 1200)
1605  {
1606  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1607  }
1608  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1609  // else use 1200m - CumDistance
1610  int FirstDistance = 0;
1611  if(CumDistance >= 1200)
1612  {
1613  FirstDistance = 100;
1614  }
1615  else
1616  {
1617  FirstDistance = 1200 - CumDistance;
1618  }
1619  if(FirstDistance < 100)
1620  {
1621  FirstDistance = 100; // don't allow < 100
1622  }
1623  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1624  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1625  if(ExitSpeedFull > 20.0)
1626  {
1627  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1628  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1629  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1630  // 4320.0 = 3.6 * 1200, .0 to make it a double
1631  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1632  }
1633  else
1634  {
1635  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1636  ContinuationAutoSigEntry.SecondDelay = 120.0;
1637  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1638  }
1639  ContinuationAutoSigEntry.AccessNumber = 0;
1640  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1642  {
1644  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1645  VectorIT++)
1646  {
1647  if(VectorIT->RouteNumber == RouteNumber)
1648  {
1649  // another train has passed out of same route so erase earlier entry
1650  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1651  break;
1652  }
1653  }
1654  }
1655  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1656  }
1658  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1659  Display->Update();
1660  // need to keep this since Update() not called for PlotSmallOutput as too slow
1661  Utilities->CallLogPop(659);
1662  return;
1663  }
1664  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1665  if(LeadElement > -1)
1666  {
1668  // changed to lead so reset early
1669  {
1670  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1672  // don't plot if zoomed out
1673  if(!Display->ZoomOutFlag)
1674  {
1676  }
1677  // covers signal resetting in same direction
1678  }
1679  }
1681  {
1682  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1683  {
1684  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1685  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1686  TPrefDirElement PrefDirElement;
1687  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1689  {
1691  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1692  }
1694  LockedVectorNumber)))
1695  {
1697  }
1698  }
1699  }
1700  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1701  {
1702  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1704  // don't plot if zoomed out
1705  if(!Display->ZoomOutFlag)
1706  {
1708  }
1709  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1710  }
1712  {
1713  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1714  {
1715  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1716  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1717  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1718  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1719  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1720  int RouteNumber;
1722  // already know it's an autosigsroute, this is just to get the RouteNumber
1723  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1724  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1725  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1726  int RouteNumber2;
1728  // already know it's an autosigsroute, this is just to get the RouteNumber
1729  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1730  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1731  {
1732  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1733  // this was in the 1.3.0 addition but without the condition
1734  }
1735  // end of 1.3.2 addition
1736  // end of 1.3.0.addition
1737  }
1738  TPrefDirElement PrefDirElement;
1739  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1741  {
1743  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1744  }
1745  }
1746  }
1747  }
1748  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1749  if(Straddle == LeadMid)
1750  {
1751  AllowedToPassRedSignal = false;
1752  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1753  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1754  if(DerailPending)
1755  // set during last GetLeadElement, but only act on it when train fully on offending point
1756  // i.e. next time Straddle reaches LeadMid
1757  {
1758  Derailed = true;
1759  DerailPending = false;
1763  Utilities->CallLogPop(657);
1764  return;
1765  }
1772  Straddle = MidLag;
1773  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1774  // LeadElement during this function (note that if stopped at signal then won't get this far)
1775  if(LeadElement > -1)
1776  {
1778  // i.e an exit continuation only
1779  // if don't exclude entry continuations then can't progress past it
1780  {
1781  LeadElement = -1;
1782  }
1783  else
1784  {
1785  GetLeadElement(0);
1786  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1788  if(Stopped())
1789  {
1790  if(TrainFailurePending) // ok, stopped so PlotElements set
1791  {
1792  TrainHasFailed(6);
1793  }
1794  Utilities->CallLogPop(658);
1795  return; // i.e. don't move forward one step if next element is a red signal
1796  }
1797  }
1798  }
1799  }
1800  if(LagElement > -1)
1801  {
1802  // below are the actions required at both half moves for LagElement > -1
1804 
1805  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1806  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1807  // need to do this for each half element
1808 
1809  TPrefDirElement PrefDirElement;
1810  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1811  {
1812  int RouteNumber; // holder for call below - not used
1814  {
1815  if(Utilities->clTransparent == TColor(0xFFFFFF))
1816  // change to black for a white background
1817  {
1819  // only applies for AutoSigs Route in case was locked & timed out
1820  }
1821  else
1822  // change to white for a dark background
1823  {
1825  // only applies for AutoSigs Route in case was locked & timed out
1826  }
1828  }
1829  }
1831  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1832  // or a train on the opposite track - needs to be replotted
1833  }
1834  // update all array values
1835  HOffset[3] = HOffset[2];
1836  HOffset[2] = HOffset[1];
1837  HOffset[1] = HOffset[0];
1838  VOffset[3] = VOffset[2];
1839  VOffset[2] = VOffset[1];
1840  VOffset[1] = VOffset[0];
1841  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1842 
1843  BackgroundPtr[3] = BackgroundPtr[2];
1844  BackgroundPtr[2] = BackgroundPtr[1];
1845  BackgroundPtr[1] = BackgroundPtr[0];
1846  BackgroundPtr[0] = TempPtr;
1847 
1848  // update headcode graphics depending on Lead entry value
1849  if(LeadElement > -1) // if Lead is -1 then stays as is
1850  {
1852  {
1853  for(int x = 0; x < 4; x++)
1854  {
1855  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1856  }
1857  }
1858  else
1859  {
1860  for(int x = 0; x < 4; x++)
1861  {
1863  }
1864  }
1865  }
1866  if(TrainMode == Timetable)
1867  {
1869  }
1870  else
1871  {
1873  }
1875 
1876  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1877  if(LeadElement > -1)
1878  {
1879  if(Straddle == MidLag)
1880  // just about to move half onto the new lead element
1881  {
1883  // pick up new background bitmap [0]
1885  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1886  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1887  // check if own ID for entry at continuation, else crashes into itself!
1888  {
1889  // OK if crossing on a bridge
1890  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1891  if(OtherTrainEntryPos == -1)
1892  {
1893  throw Exception("Error - OtherTrainEntryPos not set");
1894  }
1895  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1896  // LeadEntryPos for rear end crashes
1897  (LeadExitPos == OtherTrainEntryPos))
1898  // LeadExitPos for head-on crashes
1899  {
1901  Crashed = true; // only set if Straddle = MidLag
1902  CallingOnFlag = false;
1903  // in case was set, need to disable call on if call on button had been pressed
1904  }
1905  }
1906  else if(MidElement > -1) // will be -1 for continuation entries
1907  {
1908  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1909  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1910  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1911  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1912  int OtherTrainID = -1;
1913  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1914  {
1915  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1916  {
1917  TrainCrashedInto = OtherTrainID;
1918  Crashed = true; // only set if Straddle = MidLag
1919  CallingOnFlag = false;
1920  // in case was set, need to disable call on if call on button had been pressed
1921  }
1922  }
1923  }
1924  }
1925  else
1926  {
1928  // pick up new background bitmap [0]
1930  }
1931  PlotElement[0] = LeadElement;
1933  PlotTrainGraphic(12, 0, Display);
1934  }
1935  if(MidElement > -1)
1936  {
1937  PlotElement[2] = MidElement;
1939  PlotTrainGraphic(1, 2, Display);
1940  }
1941  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1942  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1943  if(Straddle == MidLag)
1944  {
1945  if(MidElement > -1)
1946  {
1947  PlotElement[1] = MidElement;
1949  PlotTrainGraphic(2, 1, Display);
1950  }
1951  if(LagElement > -1)
1952  {
1953  PlotElement[3] = LagElement;
1955  PlotTrainGraphic(3, 3, Display);
1956  }
1957  }
1958  else // Straddle == LeadMidLag
1959  {
1960  if(LeadElement > -1)
1961  {
1962  PlotElement[1] = LeadElement;
1964  PlotTrainGraphic(4, 1, Display);
1965  }
1966  if(MidElement > -1)
1967  {
1968  PlotElement[3] = MidElement;
1970  PlotTrainGraphic(5, 3, Display);
1971  }
1972  }
1973  if(Crashed)
1974  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1975  {
1980  // in case was set, need to disable call on if call on button had been pressed
1987  Straddle = LeadMidLag;
1988  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1989  Display->Update();
1990  // resurrected when Update() dropped from PlotOutput etc
1991  Utilities->CallLogPop(660);
1992  return;
1993  }
1994  // deal here with station stops & pass times after all replotting done but before Straddle changed
1995  if(TrainMode == Timetable)
1996  {
1997  if(Straddle == LeadMidLag)
1998  {
1999  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2000  {
2001  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2002  // to point to the location arrival entry - before a change of direction
2003  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2004  bool StopRequired = false;
2005  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2006  if(TTVPos > -1) // -1 if can't find it or if name is ""
2007  {
2008  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2009  // or a station where next element contains a train or a stop signal, if so
2010  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2011  // to test the actual track the train is on since it can't be a platform
2012  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2013  TTrackElement NextTrackElement; // default for now
2014  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2015  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2016  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2017  int NextElementEntryPos = -1;
2018  int NextElementExitPos = -1;
2019  bool TrainOnNextElement = false;
2020  bool StopSignalAtNextElement = false;
2021  if(ForwardConnection)
2022  // if no forward connection can't derive anything from it without errors
2023  {
2024  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2025  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2026  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2027  // this is only for signals so no need to worry about points ambiguity
2028  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2029  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2030  }
2031  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
2032  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2033  {
2034  if(TTVPos > 0)
2035  {
2037  ActionVectorEntryPtr += TTVPos;
2038  }
2039  if(StopRequired)
2040  {
2041  StoppedAtLocation = true;
2042  StoppedAtSignal = false;
2043  // may have been set earlier at line 925 so need to reset as
2044  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2045  // in zoom out mode
2046  if(!TrainFailed)
2047  {
2049  // pale green
2050  }
2053  {
2054  TimeTimeLocArrived = true;
2055  // used in case of later signaller control, when need to know
2056  // whether had arrived or not, to avoid sending the arrival
2057  // message twice, see TInterface::TimetableControl1Click
2058  }
2059  }
2060  else
2061  {
2063  }
2065  {
2067  }
2068  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2070  }
2071  }
2072  }
2073  }
2074  }
2075  if(Straddle == MidLag)
2076  {
2077  Straddle = LeadMidLag;
2078  FirstHalfMove = false;
2079  }
2080  else if(Straddle == LeadMidLag)
2081  {
2082  Straddle = LeadMid;
2083  FirstHalfMove = true;
2084  }
2085  else if(Straddle == LeadMid)
2086  {
2087  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2088  }
2089  if(TrainFailurePending) // ok, moving but PlotElements set above
2090  {
2091  TrainHasFailed(7);
2092  }
2093  Display->Update();
2094  // need to keep this since Update() not called for PlotSmallOutput as too slow
2095  Utilities->CallLogPop(661);
2096 }
2097 
2098 // ----------------------------------------------------------------------------
2099 
2100 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2101 {
2102  switch(CodeChar)
2103  {
2104  case '0':
2105  return(RailGraphics->Code0);
2106 
2107  case '1':
2108  return(RailGraphics->Code1);
2109 
2110  case '2':
2111  return(RailGraphics->Code2);
2112 
2113  case '3':
2114  return(RailGraphics->Code3);
2115 
2116  case '4':
2117  return(RailGraphics->Code4);
2118 
2119  case '5':
2120  return(RailGraphics->Code5);
2121 
2122  case '6':
2123  return(RailGraphics->Code6);
2124 
2125  case '7':
2126  return(RailGraphics->Code7);
2127 
2128  case '8':
2129  return(RailGraphics->Code8);
2130 
2131  case '9':
2132  return(RailGraphics->Code9);
2133 
2134  case 'A':
2135  return(RailGraphics->CodeA);
2136 
2137  case 'B':
2138  return(RailGraphics->CodeB);
2139 
2140  case 'C':
2141  return(RailGraphics->CodeC);
2142 
2143  case 'D':
2144  return(RailGraphics->CodeD);
2145 
2146  case 'E':
2147  return(RailGraphics->CodeE);
2148 
2149  case 'F':
2150  return(RailGraphics->CodeF);
2151 
2152  case 'G':
2153  return(RailGraphics->CodeG);
2154 
2155  case 'H':
2156  return(RailGraphics->CodeH);
2157 
2158  case 'I':
2159  return(RailGraphics->CodeI);
2160 
2161  case 'J':
2162  return(RailGraphics->CodeJ);
2163 
2164  case 'K':
2165  return(RailGraphics->CodeK);
2166 
2167  case 'L':
2168  return(RailGraphics->CodeL);
2169 
2170  case 'M':
2171  return(RailGraphics->CodeM);
2172 
2173  case 'N':
2174  return(RailGraphics->CodeN);
2175 
2176  case 'O':
2177  return(RailGraphics->CodeO);
2178 
2179  case 'P':
2180  return(RailGraphics->CodeP);
2181 
2182  case 'Q':
2183  return(RailGraphics->CodeQ);
2184 
2185  case 'R':
2186  return(RailGraphics->CodeR);
2187 
2188  case 'S':
2189  return(RailGraphics->CodeS);
2190 
2191  case 'T':
2192  return(RailGraphics->CodeT);
2193 
2194  case 'U':
2195  return(RailGraphics->CodeU);
2196 
2197  case 'V':
2198  return(RailGraphics->CodeV);
2199 
2200  case 'W':
2201  return(RailGraphics->CodeW);
2202 
2203  case 'X':
2204  return(RailGraphics->CodeX);
2205 
2206  case 'Y':
2207  return(RailGraphics->CodeY);
2208 
2209  case 'Z':
2210  return(RailGraphics->CodeZ);
2211 
2212  case 'a':
2213  return(RailGraphics->Code_a);
2214 
2215  case 'b':
2216  return(RailGraphics->Code_b);
2217 
2218  case 'c':
2219  return(RailGraphics->Code_c);
2220 
2221  case 'd':
2222  return(RailGraphics->Code_d);
2223 
2224  case 'e':
2225  return(RailGraphics->Code_e);
2226 
2227  case 'f':
2228  return(RailGraphics->Code_f);
2229 
2230  case 'g':
2231  return(RailGraphics->Code_g);
2232 
2233  case 'h':
2234  return(RailGraphics->Code_h);
2235 
2236  case 'i':
2237  return(RailGraphics->Code_i);
2238 
2239  case 'j':
2240  return(RailGraphics->Code_j);
2241 
2242  case 'k':
2243  return(RailGraphics->Code_k);
2244 
2245  case 'l':
2246  return(RailGraphics->Code_l);
2247 
2248  case 'm':
2249  return(RailGraphics->Code_m);
2250 
2251  case 'n':
2252  return(RailGraphics->Code_n);
2253 
2254  case 'o':
2255  return(RailGraphics->Code_o);
2256 
2257  case 'p':
2258  return(RailGraphics->Code_p);
2259 
2260  case 'q':
2261  return(RailGraphics->Code_q);
2262 
2263  case 'r':
2264  return(RailGraphics->Code_r);
2265 
2266  case 's':
2267  return(RailGraphics->Code_s);
2268 
2269  case 't':
2270  return(RailGraphics->Code_t);
2271 
2272  case 'u':
2273  return(RailGraphics->Code_u);
2274 
2275  case 'v':
2276  return(RailGraphics->Code_v);
2277 
2278  case 'w':
2279  return(RailGraphics->Code_w);
2280 
2281  case 'x':
2282  return(RailGraphics->Code_x);
2283 
2284  case 'y':
2285  return(RailGraphics->Code_y);
2286 
2287  case 'z':
2288  return(RailGraphics->Code_z);
2289 
2290  default:
2291  return(RailGraphics->TempHeadCode);
2292  }
2293 }
2294 
2295 // ----------------------------------------------------------------------------
2296 
2297 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2298 {
2299  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2300  if(Code.Length() != 4)
2301  {
2302  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2303  }
2304  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2305  {
2306  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2307  }
2308  if(BackgroundColour != clB5G5R5)
2309  // i.e. not the basic graphic colour as loaded from resource file
2310  {
2311  for(int x = 0; x < 4; x++)
2312  {
2314  }
2315  }
2316  Utilities->CallLogPop(1484);
2317 }
2318 
2319 // ----------------------------------------------------------------------------
2320 
2321 void TTrain::GetLeadElement(int Caller)
2322 // assumes Mid & Lag already set, sets LeadElement,
2323 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2324 {
2325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2326  DerailPending = false;
2330  {
2331  // attr 0=straight, - links 0 & 1 (0 = lead)
2332  // attr 1=diverging, - links 2 & 3 (2 = lead)
2333  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2334  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2335 
2336  // if enter at lead, exit at whatever attr set at
2337  // if enter at lag, exit at lead, but set derail wrt attribute
2338  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2339  {
2340  LeadExitPos = 1;
2341  }
2342 
2343  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2344  // best to be on safe side
2345  else if(LeadEntryPos == 0)
2346  {
2347  LeadEntryPos = 2;
2348  LeadExitPos = 3;
2349  }
2350  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2351  {
2352  LeadEntryPos = 0;
2353  LeadExitPos = 1;
2354  }
2355  else if(LeadEntryPos == 2)
2356  {
2357  LeadExitPos = 3;
2358  }
2359 
2360  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2361  {
2362  LeadExitPos = 0;
2363  }
2364  else if(LeadEntryPos == 1)
2365  {
2366  LeadExitPos = 0;
2367  DerailPending = true;
2368  }
2369  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2370  {
2371  LeadExitPos = 0;
2372  DerailPending = true;
2373  }
2374  else if(LeadEntryPos == 3)
2375  {
2376  LeadExitPos = 0;
2377  }
2378  }
2379  else if(LeadEntryPos == 0)
2380  {
2381  LeadExitPos = 1;
2382  }
2383  else if(LeadEntryPos == 1)
2384  {
2385  LeadExitPos = 0;
2386  }
2387  else if(LeadEntryPos == 2)
2388  {
2389  LeadExitPos = 3;
2390  }
2391  else if(LeadEntryPos == 3)
2392  {
2393  LeadExitPos = 2;
2394  }
2395  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2396 /* signal check moved to Update() function
2397  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2398  && (TrackElement.Attribute == 0))//0 = red
2399  {
2400  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2401  }
2402  else
2403  {
2404  StoppedAtSignal = false;
2405  }
2406 */
2407  Utilities->CallLogPop(662);
2408 }
2409 
2410 // ----------------------------------------------------------------------------
2411 
2412 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2413 {
2414  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2415  switch(Link)
2416  {
2417  case 1:
2418  {
2419  HOffset = 0;
2420  VOffset = 0;
2421  break;
2422  }
2423 
2424  case 2:
2425  {
2426  HOffset = 4;
2427  VOffset = 0;
2428  break;
2429  }
2430 
2431  case 3:
2432  {
2433  HOffset = 8;
2434  VOffset = 0;
2435  break;
2436  }
2437 
2438  case 4:
2439  {
2440  HOffset = 0;
2441  VOffset = 4;
2442  break;
2443  }
2444 
2445  case 6:
2446  {
2447  HOffset = 8;
2448  VOffset = 4;
2449  break;
2450  }
2451 
2452  case 7:
2453  {
2454  HOffset = 0;
2455  VOffset = 8;
2456  break;
2457  }
2458 
2459  case 8:
2460  {
2461  HOffset = 4;
2462  VOffset = 8;
2463  break;
2464  }
2465 
2466  case 9:
2467  {
2468  HOffset = 8;
2469  VOffset = 8;
2470  break;
2471  }
2472 
2473  default:
2474  {
2475  throw Exception("Error in GetOffsetValues - Link value wrong");
2476  }
2477  }
2478  Utilities->CallLogPop(674);
2479 }
2480 
2481 // ---------------------------------------------------------------------------
2482 
2483 bool TTrain::LowEntryValue(int EntryLink) const
2484 {
2485 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2486  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2487 */
2488  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2489  {
2490  return(true);
2491  }
2492  else
2493  {
2494  return(false);
2495  }
2496 }
2497 
2498 // ---------------------------------------------------------------------------
2499 
2500 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2501 {
2502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2503  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2504  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2505  // default values
2506  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2507 
2508  TAllRoutes::TRouteType RouteType;
2509 
2510  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2511 
2512  TRect SourceRect, DestRect;
2513 
2514  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2515  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2516  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2517  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2518 
2519  TempGraphic->PixelFormat = pf8bit;
2520  TempGraphic->Width = 16;
2521  TempGraphic->Height = 16;
2522  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2523 
2524  if(TempElement.TrackType == Points)
2525  {
2526  TempGraphic->Assign(TempElement.GraphicPtr);
2527  TempGraphic->Transparent = true;
2528  TempGraphic->TransparentColor = Utilities->clTransparent;
2529  if(RouteType == TAllRoutes::AutoSigsRoute)
2530  {
2531  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2532  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2533  }
2534  else
2535  {
2536  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2537  }
2538  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2539  }
2540  else if(TempElement.TrackType == GapJump) // plot set gap
2541  {
2542  if(TempElement.SpeedTag == 88)
2543  {
2544  TempGraphic->Assign(RailGraphics->gl88set);
2545  }
2546  else if(TempElement.SpeedTag == 89)
2547  {
2548  TempGraphic->Assign(RailGraphics->gl89set);
2549  }
2550  else if(TempElement.SpeedTag == 90)
2551  {
2552  TempGraphic->Assign(RailGraphics->gl90set);
2553  }
2554  else if(TempElement.SpeedTag == 91)
2555  {
2556  TempGraphic->Assign(RailGraphics->gl91set);
2557  }
2558  else if(TempElement.SpeedTag == 92)
2559  {
2560  TempGraphic->Assign(RailGraphics->gl92set);
2561  }
2562  else if(TempElement.SpeedTag == 93)
2563  {
2564  TempGraphic->Assign(RailGraphics->bm93set);
2565  }
2566  else if(TempElement.SpeedTag == 94)
2567  {
2568  TempGraphic->Assign(RailGraphics->bm94set);
2569  }
2570  else if(TempElement.SpeedTag == 95)
2571  {
2572  TempGraphic->Assign(RailGraphics->gl95set);
2573  }
2574  TempGraphic->Transparent = true;
2575  TempGraphic->TransparentColor = Utilities->clTransparent;
2576  if(RouteType == TAllRoutes::AutoSigsRoute)
2577  {
2578  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2579  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2580  }
2581  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2582  }
2583  // new for version 0.6
2584  else if(TempElement.TrackType == SignalPost)
2585  {
2586  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2587  {
2588  for(int x = 0; x < 40; x++)
2589  {
2590  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2591  // need to stop aspect
2592  {
2593  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2594  break;
2595  }
2596  }
2597  }
2598  else // normal signal
2599  {
2600  TempGraphic->Assign(TempElement.GraphicPtr);
2601  // GraphicPtr set to normal signal in a signal track element
2602  }
2603  TempGraphic->Transparent = true;
2604  TempGraphic->TransparentColor = Utilities->clTransparent;
2605  if(RouteType == TAllRoutes::AutoSigsRoute)
2606  {
2607  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2608  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2609  }
2610  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2611  }
2612  else
2613  {
2614  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2615  // can't name points gaps or signals so 'else' OK
2616  bool FoundFlag;
2617  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2618  if(FoundFlag)
2619  {
2621  {
2622  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2623  TempGraphic->Assign(RailGraphics->bmName);
2624  TempGraphic->Transparent = true;
2625  TempGraphic->TransparentColor = Utilities->clTransparent;
2626  if(RouteType == TAllRoutes::AutoSigsRoute)
2627  {
2628  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2629  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2630  }
2631  else
2632  {
2633  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2634  }
2635  // draw track on top
2636  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2637  }
2638  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2639  {
2640  TempGraphic->Assign(TempElement.GraphicPtr);
2641  TempGraphic->Transparent = true;
2642  TempGraphic->TransparentColor = Utilities->clTransparent;
2643  // note that can't be an AutoSigsRoute
2644  // now overlay the LC central portion
2645  int BDVectorPos = -1; //not used
2646  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2647  {
2648  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2649  }
2650  else
2651  {
2652  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2653  }
2654  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2655  }
2656  else
2657  {
2658  TempGraphic->Assign(TempElement.GraphicPtr);
2659  TempGraphic->Transparent = true;
2660  TempGraphic->TransparentColor = Utilities->clTransparent;
2661  if(RouteType == TAllRoutes::AutoSigsRoute)
2662  {
2663  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2664  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2665  }
2666  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2667  }
2668  }
2669  else
2670  {
2671  TempGraphic->Assign(TempElement.GraphicPtr);
2672  TempGraphic->Transparent = true;
2673  TempGraphic->TransparentColor = Utilities->clTransparent;
2674  if(RouteType == TAllRoutes::AutoSigsRoute)
2675  {
2676  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2677  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2678  }
2679  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2680  }
2681  }
2682  delete TempGraphic;
2683  Utilities->CallLogPop(675);
2684 }
2685 
2686 // ---------------------------------------------------------------------------
2687 
2688 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2689 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2690 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2691 /*
2692 
2693  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2694  {
2695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2696  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2697  TAllRoutes::TRouteType RouteType;
2698  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2699  // default values
2700  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2701  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2702  TRect SourceRect, DestRect, ScreenSourceRect;
2703  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2704 
2705  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2706  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2707  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2708 
2709  //add text & user graphics if any to *GraphicPtr prior to adding the track
2710  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2711  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2712  int Right = Left + 8;
2713  int Bottom = Top + 8;
2714  ScreenSourceRect.init(Left, Top, Right, Bottom);
2715  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2716  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2717 
2718  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2719  TempGraphic->PixelFormat = pf8bit;
2720  TempGraphic->Width = 16;
2721  TempGraphic->Height = 16;
2722 
2723  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2724  SourceGraphic->PixelFormat = pf8bit;
2725  SourceGraphic->Width = 16;
2726  SourceGraphic->Height = 16;
2727  SourceGraphic->Transparent = true;
2728  SourceGraphic->TransparentColor = Utilities->clTransparent;
2729 
2730  if (TempElement.TrackType == Points)
2731  {
2732  TempGraphic->Assign(TempElement.GraphicPtr);
2733  TempGraphic->Transparent = true;
2734  TempGraphic->TransparentColor = Utilities->clTransparent;
2735  if (RouteType == TAllRoutes::AutoSigsRoute)
2736  {
2737  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2738  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2739  }
2740  else
2741  {
2742  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2743  }
2744  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2745  }
2746  else if (TempElement.TrackType == GapJump) // plot set gap
2747  {
2748  if (TempElement.SpeedTag == 88)
2749  TempGraphic->Assign(RailGraphics->gl88set);
2750  else if (TempElement.SpeedTag == 89)
2751  TempGraphic->Assign(RailGraphics->gl89set);
2752  else if (TempElement.SpeedTag == 90)
2753  TempGraphic->Assign(RailGraphics->gl90set);
2754  else if (TempElement.SpeedTag == 91)
2755  TempGraphic->Assign(RailGraphics->gl91set);
2756  else if (TempElement.SpeedTag == 92)
2757  TempGraphic->Assign(RailGraphics->gl92set);
2758  else if (TempElement.SpeedTag == 93)
2759  TempGraphic->Assign(RailGraphics->bm93set);
2760  else if (TempElement.SpeedTag == 94)
2761  TempGraphic->Assign(RailGraphics->bm94set);
2762  else if (TempElement.SpeedTag == 95)
2763  TempGraphic->Assign(RailGraphics->gl95set);
2764  TempGraphic->Transparent = true;
2765  TempGraphic->TransparentColor = Utilities->clTransparent;
2766  if (RouteType == TAllRoutes::AutoSigsRoute) {
2767  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2768  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2769  }
2770  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2771  }
2772  // new for version 0.6
2773  else if (TempElement.TrackType == SignalPost)
2774  {
2775  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2776  {
2777  for (int x = 0; x < 40; x++)
2778  {
2779  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2780  // need to stop aspect
2781  {
2782  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2783  break;
2784  }
2785  }
2786  }
2787  else // normal signal
2788  {
2789  TempGraphic->Assign(TempElement.GraphicPtr);
2790  // GraphicPtr set to normal signal in a signal track element
2791  }
2792  TempGraphic->Transparent = true;
2793  TempGraphic->TransparentColor = Utilities->clTransparent;
2794  if (RouteType == TAllRoutes::AutoSigsRoute) {
2795  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2796  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2797  }
2798  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2799  }
2800  else {
2801  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2802  // can't name points gaps or signals so 'else' OK
2803  bool FoundFlag;
2804  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2805  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2806  if (FoundFlag)
2807  {
2808  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2809  {
2810  GraphicPtr->Canvas->CopyRect(DestRect,
2811  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2812  TempGraphic->Assign(RailGraphics->bmName);
2813  TempGraphic->Transparent = true;
2814  TempGraphic->TransparentColor = Utilities->clTransparent;
2815  if (RouteType == TAllRoutes::AutoSigsRoute)
2816  {
2817  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2818  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2819  }
2820  else
2821  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2822  // draw track on top
2823  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2824  SourceRect);
2825  }
2826  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2827  TempGraphic->Assign(TempElement.GraphicPtr);
2828  TempGraphic->Transparent = true;
2829  TempGraphic->TransparentColor = Utilities->clTransparent;
2830  // note that can't be an AutoSigsRoute
2831  // now overlay the LC central portion
2832  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2833  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2834  SourceRect);
2835  }
2836  else {
2837  TempGraphic->Assign(TempElement.GraphicPtr);
2838  TempGraphic->Transparent = true;
2839  TempGraphic->TransparentColor = Utilities->clTransparent;
2840  if (RouteType == TAllRoutes::AutoSigsRoute) {
2841  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2842  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2843  }
2844  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2845  SourceRect);
2846  }
2847  }
2848  else {
2849  TempGraphic->Assign(TempElement.GraphicPtr);
2850  TempGraphic->Transparent = true;
2851  TempGraphic->TransparentColor = Utilities->clTransparent;
2852  if (RouteType == TAllRoutes::AutoSigsRoute) {
2853  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2854  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2855  }
2856  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2857  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2858  }
2859  }
2860  delete TempGraphic;
2861  delete SourceGraphic;
2862  Utilities->CallLogPop();
2863  }
2864 */
2865 // ---------------------------------------------------------------------------
2866 
2867 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2868 {
2869  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2870  if(PlotElement[ArrayNumber] == -1)
2871  {
2872  Utilities->CallLogPop(676);
2873  return; // not plotted yet
2874  }
2875  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2876  // set before plot so gap flashing stops first
2877  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2878  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2879  // Only need to set ID for leading element, stays set until train finally leaves the element
2880  Plotted = true;
2881  Utilities->CallLogPop(677);
2882 }
2883 
2884 // ---------------------------------------------------------------------------
2885 
2886 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2887 {
2888  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2889  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2890 }
2891 
2892 // ---------------------------------------------------------------------------
2893 
2894 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2895 {
2896  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2897  HeadCode);
2898  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2899  {
2900  Utilities->CallLogPop(678);
2901  return(true);
2902  }
2903  else
2904  {
2905  Utilities->CallLogPop(679);
2906  return(false);
2907  }
2908 }
2909 
2910 // ---------------------------------------------------------------------------
2911 
2912 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2913 {
2914  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2915  "," + HeadCode);
2916  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2917  {
2918  Utilities->CallLogPop(680);
2919  return(true);
2920  }
2921  else
2922  {
2923  Utilities->CallLogPop(681);
2924  return(false);
2925  }
2926 }
2927 
2928 // ---------------------------------------------------------------------------
2929 
2930 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2931 // test whether this train on a bridge on trackpos 0 & 1
2932 {
2933  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2934  HeadCode);
2935  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2936  {
2937  Utilities->CallLogPop(682);
2938  return(false);
2939  }
2940  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2941  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2942  {
2943  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2944  {
2945  throw Exception("Error, same train on two different bridge tracks");
2946  }
2947  else
2948  {
2949  Utilities->CallLogPop(684);
2950  return(true);
2951  }
2952  }
2953  else
2954  {
2955  Utilities->CallLogPop(685);
2956  return(false);
2957  }
2958 }
2959 
2960 // ---------------------------------------------------------------------------
2961 
2962 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2963 // test whether this train on a bridge on trackpos 2 & 3
2964 {
2965  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2966  HeadCode);
2967  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2968  {
2969  Utilities->CallLogPop(686);
2970  return(false);
2971  }
2972  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2973  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2974  {
2975  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2976  Utilities->CallLogPop(687);
2977  return(true);
2978  }
2979  else
2980  {
2981  Utilities->CallLogPop(688);
2982  return(false);
2983  }
2984 }
2985 
2986 // ---------------------------------------------------------------------------
2987 
2988 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2989 {
2990  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2991  AnsiString(EntryPos) + "," + HeadCode);
2992  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2993 
2994  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2995  if(Track->GapFlashFlag)
2996  {
2998  {
3001  Track->GapFlashFlag = false;
3002  }
3003  }
3004  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3005  {
3006  if(EntryPos == -1)
3007  {
3008  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3009  }
3010  if(EntryPos < 2)
3011  {
3012  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3013  }
3014  else
3015  {
3016  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3017  }
3018  }
3019  Utilities->CallLogPop(690);
3020 }
3021 
3022 // ---------------------------------------------------------------------------
3023 
3024 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3025 {
3026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3027  AnsiString(EntryPos) + "," + HeadCode);
3028  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3029  {
3030  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3031  }
3032  else
3033  {
3034  if(EntryPos == -1)
3035  {
3036  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3037  }
3038  if(EntryPos < 2)
3039  {
3040  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3041  }
3042  else
3043  {
3044  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3045  }
3046  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3047  // i.e. other train on track 2&3
3048  {
3049  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3050  }
3051  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3052  // i.e. other train on track 1&2
3053  {
3054  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3055  }
3056  else
3057  {
3058  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3059  }
3060  }
3061  Utilities->CallLogPop(691);
3062 }
3063 
3064 // ---------------------------------------------------------------------------
3065 
3066 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3067 {
3068  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3069  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3070  int LockedVectorNumber;
3071 
3072  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3073  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3074  {
3075  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3076  Utilities->CallLogPop(692);
3077  return;
3078  }
3079  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3080  // i.e other track is in a marked route
3081  // LinkPos doesn't have to be the entry position for the above check
3082  {
3083  TRect SourceRect, DestRect;
3084  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3085  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3086  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3087  // identify the route element for the other track
3088  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3089  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3090  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3091  int FirstELink, SecondELink = -1;
3092  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3093  // must be at least one
3094  if(RoutePair2.first > -1)
3095  {
3096  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3097  }
3098  TPrefDirElement RouteElement;
3099  // Graphics::TBitmap *RouteGraphic;
3100  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3101  // i.e. other track is in RoutePair2
3102  {
3103  if(SecondELink == -1)
3104  {
3105  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3106  }
3107  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3108  // error if both have same Link number
3109  {
3110  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3111  }
3112  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3113  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3114  }
3115  else // other track is in RoutePair1
3116  {
3117  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3118  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3119  }
3120  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3121  DestGraphic->PixelFormat = pf8bit;
3122  DestGraphic->Width = 8;
3123  DestGraphic->Height = 8;
3124  DestGraphic->Transparent = true;
3125  // has to be transparent or will overwrite the track that the train has just left
3126  DestGraphic->TransparentColor = Utilities->clTransparent;
3127  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3128  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3129  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3130  // plot locked route marker for other route if appropriate
3131  TPrefDirElement PrefDirElement; // holder for next call, unused
3132  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3133  if(StraddleValue == LeadMidLag)
3134  {
3136  PrefDirElement, LockedVectorNumber))
3137  {
3138  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3139  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3140  }
3141  }
3142  delete DestGraphic;
3143  }
3144  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3145  // also can only be a bridge or trains either have already or soon will crash
3146  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3147  {
3148  Utilities->CallLogPop(695);
3149  return;
3150  }
3151  if(ElementEntryPos > 1) // other train is on track 01
3152  {
3153  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3154  {
3156  }
3157  }
3158  else // other train is on track 23
3159  {
3160  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3161  {
3163  }
3164  }
3165  Utilities->CallLogPop(696);
3166 }
3167 
3168 // ---------------------------------------------------------------------------
3169 
3170 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3171 {
3172  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3173  AnsiString(EntryPos) + "," + HeadCode);
3174  int RouteNumber;
3175  bool WrongRoute = false;
3176  TPrefDirElement RouteElement;
3178  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3179 
3180  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3181  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3182  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3183  {
3184  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3185  {
3186  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3187  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3188  {
3189  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3190  {
3191  // don't call for stub end routes
3193  }
3194  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3195  Utilities->CallLogPop(697);
3196  return;
3197  }
3198  }
3199  // also need to check for a route on a crossing diagonal
3200  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3201  int LinkNumber = TrackElement.Link[EntryPos];
3202  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3203  {
3204  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3205  {
3206  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3207  bool LogActionErrorCalled = false;
3208  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3209  if(LinkNumber == 1)
3210  {
3211  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3212  {
3213  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3214  {
3215  // don't call for stub end routes
3217  LogActionErrorCalled = true;
3218  }
3219  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3220  }
3221  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3222  // not else in case have different routes on each diagonal, though shouldn't be possible
3223  {
3224  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3225  {
3226  // don't call for stub end routes
3228  }
3229  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3230  }
3231  }
3232 
3233  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3234  else if(LinkNumber == 3)
3235  {
3236  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3237  {
3238  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3239  {
3240  // don't call for stub end routes
3242  LogActionErrorCalled = true;
3243  }
3244  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3245  }
3246  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3247  // not else in case have different routes on each diagonal, though shouldn't be possible
3248  {
3249  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3250  {
3251  // don't call for stub end routes
3253  }
3254  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3255  }
3256  }
3257 
3258  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3259  else if(LinkNumber == 7)
3260  {
3261  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3262  {
3263  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3264  {
3265  // don't call for stub end routes
3267  LogActionErrorCalled = true;
3268  }
3269  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3270  }
3271  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3272  // not else in case have different routes on each diagonal, though shouldn't be possible
3273  {
3274  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3275  {
3276  // don't call for stub end routes
3278  }
3279  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3280  }
3281  }
3282 
3283  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3284  else if(LinkNumber == 9)
3285  {
3286  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3287  {
3288  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3289  {
3290  // don't call for stub end routes
3292  LogActionErrorCalled = true;
3293  }
3294  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3295  }
3296  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3297  // not else in case have different routes on each diagonal, though shouldn't be possible
3298  {
3299  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3300  {
3301  // don't call for stub end routes
3303  }
3304  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3305  }
3306  }
3307  }
3308  }
3309  Utilities->CallLogPop(698);
3310  return; // no route on other track or no other track
3311  }
3312  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3313  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3314  {
3315  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3316  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new after v2.4.1 for points check - Xeon repoted it 30/05/20. He found that for routes that
3317  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3318  {
3319  if(RouteElement.GetELinkPos() == EntryPos)
3320  {
3321  Utilities->CallLogPop(699);
3322  return; // right direction
3323  }
3324  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3325  {
3326  Utilities->CallLogPop(700);
3327  return; // right direction (points)
3328  }
3329  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3330  {
3331  Utilities->CallLogPop(701);
3332  return; // right direction (points)
3333  }
3334  else if(RouteElement.GetXLinkPos() == EntryPos)
3335  {
3336  WrongRoute = true;
3337  break; // wrong direction
3338  }
3339  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3340  {
3341  WrongRoute = true;
3342  break; // wrong direction
3343  }
3344  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3345  {
3346  WrongRoute = true;
3347  break; // wrong direction
3348  }
3349  }
3350  }
3351  if(!WrongRoute)
3352  {
3353  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3354  }
3355  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3356  {
3357  // don't call for stub end routes
3359  }
3360  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3361  Utilities->CallLogPop(703);
3362 }
3363 
3364 // ---------------------------------------------------------------------------
3365 
3366 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3367 {
3368  if(BackgroundColour == NewBackgroundColour)
3369  {
3370  return; // don't replot if already correct
3371 
3372  }
3373  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3374  bool ColourError = false, ColourError2 = false;
3375 
3376  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3377  if(ColourError)
3378  {
3379  ColourError2 = true;
3380  }
3381  for(int x = 0; x < 4; x++)
3382  {
3383  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3384  if(ColourError)
3385  {
3386  ColourError2 = true;
3387  }
3388  }
3389  if(ColourError2)
3390  {
3392  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3393  }
3394  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3395  // of motion
3396  for(int x = 0; x < 4; x++)
3397  {
3398  PlotTrainGraphic(6, x, Disp);
3399  }
3400  BackgroundColour = NewBackgroundColour;
3401  Display->Update();
3402  // need to keep this since Update() not called for PlotSmallOutput as too slow
3403  Utilities->CallLogPop(704);
3404 }
3405 
3406 // ---------------------------------------------------------------------------
3407 
3408 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3409 /*
3410 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3411 
3412 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3413 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3414 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3415 full-element moves.
3416 
3417 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3418 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3419 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3420 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3421 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3422 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3423 In this case set the brake rate to maximum to stop as soon as possible.
3424 
3425 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3426 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3427 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3428 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3429 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3430 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3431 
3432 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3433 first to see whether buffers or continuation) in turn is examined: first the length of the
3434 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3435 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3436 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3437 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3438 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3439 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3440 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3441 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3442 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3443 siding then again emeregency braking may be necessary and a crash may result.
3444 
3445 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3446 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3447 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3448 buffer, then the train accelerates for half the element and brakes for the other half.
3449 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3450 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3451 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3452 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3453 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3454 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3455 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3456 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3457 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3458 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3459 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3460 
3461 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3462 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3463 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3464 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3465 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3466 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3467 MaxBrakeRate.
3468 
3469 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3470 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3471 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3472 
3473 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3474 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3475 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3476 
3477 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3478 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3479 when Straddle == LeadMidLag
3480 */
3481 {
3482  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3483  AnsiString(EntryPos) + "," + HeadCode);
3484  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3485  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3486  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3487  TrainInFrontInSignallerModeFlag = false;
3488  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3489  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3490  bool SignallerStopRequired = false;
3491 
3493  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3494 
3495  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3496 
3497  OneLengthAccelDecel = false;
3498  BrakeRate = 0;
3499 
3500 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3501  if(CurrentTrackVectorPosition > -1)
3502  {
3503  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3504  {
3505  if((EntryPos == 0) || (EntryPos == 2))
3506  {
3507  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3508  {
3509  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3510  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3511  }
3512  else
3513  {
3514  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3515  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3516  }
3517  }
3518  else if(EntryPos == 1)
3519  {
3520  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3521  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3522  }
3523  else // == 3
3524  {
3525  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3526  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3527  }
3528  }
3529  else
3530  {
3531  if(EntryPos > 1)
3532  {
3533  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3534  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3535  }
3536  else
3537  {
3538  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3539  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3540  }
3541  }
3542  EntryHalfLength = CurrentElementHalfLength;
3543  FrontElementLength = 2 * CurrentElementHalfLength;
3544  }
3545  else
3546  {
3547  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3548  }
3549  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3550  {
3551  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3552  }
3553  // check if zero entry speed with another train directly in front & if so remain stopped
3554  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3555  {
3556  EntrySpeed = 0;
3557  ExitSpeedHalf = 0;
3558  ExitSpeedFull = 0;
3559  MaxExitSpeed = 0;
3560  BrakeRate = 0;
3561  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3562  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3563  StoppedForTrainInFront = true;
3564  Utilities->CallLogPop(705);
3565  return;
3566  }
3567  // new at v2.4.0 - check for stopped and zero power
3568  if((EntrySpeed < 1) && PowerAtRail < 1)
3569  {
3570  EntrySpeed = 0;
3571  ExitSpeedHalf = 0;
3572  ExitSpeedFull = 0;
3573  MaxExitSpeed = 0;
3574  BrakeRate = 0;
3575  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3576  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3577  StoppedWithoutPower = true;
3578  Utilities->CallLogPop(2125);
3579  return;
3580  }
3581 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3582  if(BeingCalledOn)
3583  {
3584  LimitingSpeed = CallOnMaxSpeed;
3585  }
3586  else
3587  {
3588  LimitingSpeed = MaximumSpeedLimit;
3589  }
3590  if(LimitingSpeed > FrontElementSpeedLimit)
3591  {
3592  LimitingSpeed = FrontElementSpeedLimit;
3593  }
3594  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3595  {
3596  LimitingSpeed = MaxRunningSpeed;
3597  }
3598  FrontElementMaxSpeed = LimitingSpeed;
3599 
3600 /*
3601  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3602  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3603  (2) V/3.6 = U/3.6 - FT;
3604  (3) S = UT/3.6 - 0.5FT^2
3605  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3606 
3607  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3608  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3609  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3610  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3611  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3612 
3613  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3614  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3615  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3616 */
3617 
3618 // check if running past a red signal without permission
3619  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3620  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3621  {
3622  SPADFlag = true; // user has to intervene to reset & restart after spad
3623  }
3624  if(!SPADFlag)
3625  {
3626  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3627 
3628  double ExitSpeedAtMaxBraking;
3629  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3630  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3632  {
3633  ExitSpeedAtMaxBraking = 0;
3634  }
3635  else
3636  {
3637  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3638  }
3639  double SpeedToUse;
3640  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added after v2.4.1 because trains entering at a continuation with zero (or very low) speed
3641  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3642  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3643  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3644  {
3645  SpeedToUse = ExitSpeedAtMaxBraking;
3646  }
3647  else
3648  {
3649  SpeedToUse = LimitingSpeed;
3650  }
3651  if(ExitSpeedFull > SpeedToUse)
3652  {
3653  ExitSpeedFull = SpeedToUse;
3654  }
3655  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3656  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3657 
3658  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3659  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3660  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3661 
3662  do
3663  {
3664  RedSignalFlag = false;
3665  BuffersFlag = false;
3666  StationFlag = false;
3667  BuffersOrContinuationNowFlag = false;
3668  ContinuationNextFlag = false;
3669  // have to reset this after the above test
3670  // add current element length to CumulativeLength
3671  CumulativeLength += (2 * CurrentElementHalfLength);
3672  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3673  {
3674  SignallerStopRequired = true;
3675  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3676  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3677  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3678  if(SignallerStopBrakeRate < TempBR)
3679  {
3680  SignallerStopBrakeRate = TempBR;
3681  }
3682  }
3683  // first check for stops within the length of the current element, where don't want any more checks & don't want
3684  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3685  // during the last loop when the NextTrackVectorPosition was the signal.
3686 
3687  // check if current element is a buffer
3688  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3689  {
3690  // no need to add in the length of this element to CumulativeLength as already included
3691  BuffersFlag = true;
3692  }
3693  // check if current element is a station stop
3694  if(TrainMode == Timetable)
3695  {
3696  bool StopRequired = false;
3697  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3698  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3699  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3700  {
3701  // no need to add in the length of element to CumulativeLength
3702  if(StopRequired)
3703  {
3704  StationFlag = true;
3705  }
3706  }
3707  }
3708  else
3709  {
3710  StationFlag = false;
3711  }
3712  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3713  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3714  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3715  {
3716  BuffersOrContinuationNowFlag = true;
3717  }
3718  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3719  {
3720  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3721  {
3722  if((EntryPos == 0) || (EntryPos == 2))
3723  {
3724  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3725  {
3726  ExitPos = 1;
3727  }
3728  else
3729  {
3730  ExitPos = 3;
3731  }
3732  }
3733  else
3734  {
3735  ExitPos = 0;
3736  }
3737  }
3738  else
3739  {
3740  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3741  }
3742  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3743  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3744  if(NextTrackVectorPosition > -1)
3745  {
3746  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3747  // this test & section added at v0.6
3748  {
3749  if((NextEntryPos == 0) || (NextEntryPos == 2))
3750  {
3751  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3752  {
3753  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3754  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3755  }
3756  else
3757  {
3758  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3759  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3760  }
3761  }
3762  else if(NextEntryPos == 1)
3763  {
3764  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3765  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3766  }
3767  else // == 3
3768  {
3769  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3770  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3771  }
3772  }
3773  else
3774  {
3775  if(NextEntryPos > 1)
3776  {
3777  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3778  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3779  }
3780  else
3781  {
3782  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3783  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3784  }
3785  }
3786  }
3787  else
3788  {
3789  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3790  }
3791  // now check for stops, first cover those where don't want to add in length of next element
3792  // check if next element is a red signal - Attr 0,
3793  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3794  // CurrentTrackVectorPosition not NextTrackVectorPosition
3795  bool StopRequired;
3796  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3797  {
3798  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3799  {
3800  // no need to add in the length of element to CumulativeLength
3801  RedSignalFlag = true;
3802  }
3803  // next element is a red signal
3804  }
3805  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3806  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3807  // at least one platform element free
3809  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3810  NextEntryPos, TrainID))
3811  {
3812  // no need to add in the length of element to CumulativeLength
3813  if(StopRequired)
3814  {
3815  StationFlag = true;
3816  }
3817  }
3818  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3819  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3820  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3821  {
3822  // no need to add in the length of element to CumulativeLength
3823  TrainInFrontInSignallerModeFlag = true;
3824  }
3825  // check if next element is a buffer
3826  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3827  {
3828  // need to add in the length of that element to CumulativeLength
3829  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3830  BuffersFlag = true;
3831  }
3832  // check if next element is a station stop
3834  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3835  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3836  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3837  {
3838  // need to add in the length of that element to CumulativeLength if a stop required
3839  if(StopRequired)
3840  {
3841  StationFlag = true;
3842  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3843  }
3844  }
3845  }
3846  //now can decide whether need to stop over CumulativeLength
3847  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3848  {
3849  // have to come to a stop over CumulativeLength
3850  if(CumulativeLength == FrontElementLength)
3851  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3852  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3853  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3854  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3855  // if not calc speed at halfway point & if less than above set half speed to this value;
3856  // use constant acceleration in calculating half time point
3857  {
3858  MaxExitSpeed = 0;
3859  double MaxHalfSpeed;
3860  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3861  // have to halve the element length, & can't be zero or negative so no need to test
3862  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3863  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3864  {
3865  MaxHalfSpeed = FrontElementMaxSpeed;
3866  }
3867  else
3868  {
3869  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3870  }
3871  if(MaxHalfSpeed > (2 * EntrySpeed))
3872  // use 2x to prevent kangarooing at last element when had
3873  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3874  {
3875  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3876  0.333334);
3877  bool HalfSpeedLimited = false;
3878  if(MaxHalfSpeed < ExitSpeedHalf)
3879  {
3880  ExitSpeedHalf = MaxHalfSpeed;
3881  HalfSpeedLimited = true;
3882  }
3883  if(PowerAtRail > 1)
3884  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3885  {
3886  // [km/h/3.6 = m/s]
3887  ExitTimeHalf =
3888  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3889  }
3890  else
3891  {
3892  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3893  }
3894  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3895  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3896  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3897  // by a long braking period
3898  ExitSpeedFull = 0;
3899  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3900  if(TempBrakeRate > MaxBrakeRate)
3901  {
3902  TempBrakeRate = MaxBrakeRate;
3903  }
3904  // shouldn't be but leave in anyway
3905  if(TempBrakeRate > BrakeRate)
3906  {
3907  BrakeRate = TempBrakeRate;
3908  }
3909  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3910  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3911  if(HalfSpeedLimited)
3912  // this is the change referred to above
3913  {
3914  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3915  ExitTimeHalf = EntryTime + BrakingTime;
3916  ExitTimeFull = ExitTimeHalf + BrakingTime;
3917  }
3918  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3919  Utilities->CallLogPop(1095);
3920  return;
3921  }
3922  }
3923  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3924  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3925  // calc th, tf, sh, & sf
3926  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3927  if(TempBrakeRate > MaxBrakeRate)
3928  {
3929  TempBrakeRate = MaxBrakeRate;
3930  }
3931  if(TempBrakeRate > BrakeRate)
3932  {
3933  BrakeRate = TempBrakeRate;
3934  }
3935  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3936  if(SignallerStopRequired)
3937  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3938  {
3940  {
3942  // this prevents the brakerate from reducing for a signaller stop
3943  // regardless of other conditions that may change as progress round the loop
3944  }
3945  }
3947  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3948  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3949  {
3951  }
3952  int TempMaxExitSpeed;
3953  // calc current value & if less than MaxExitSpeed set that to this
3954  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3955  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3956  {
3957  MaxExitSpeedAtHalfBraking = 0;
3958  }
3959  else
3960  {
3961  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3962  }
3963  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3964  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3965  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3966  {
3967  TempMaxExitSpeed = FrontElementMaxSpeed;
3968  }
3969  else
3970  {
3971  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3972  }
3973  if(TempMaxExitSpeed < MaxExitSpeed)
3974  {
3975  MaxExitSpeed = TempMaxExitSpeed;
3976  }
3977  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3978  // Cumulativelength, and Cumulativelength
3979 
3980  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3981  {
3982  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3983  if(ExitSpeedHalfSquared < 10)
3984  {
3985  ExitSpeedHalf = 0;
3986  }
3987  else
3988  {
3989  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3990  }
3991  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3992  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3993  if(ExitSpeedFullSquared < 10)
3994  {
3995  ExitSpeedFull = 0;
3996  }
3997  else
3998  {
3999  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4000  }
4001  if((StationFlag) && (CumulativeLength == FrontElementLength))
4002  {
4003  ExitSpeedFull = 0;
4004  // force a stop for station (not for buffers or red signal)
4005  }
4006  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4007  }
4008  // new condition at v2.4.0
4009  else if(PowerAtRail <= 1)
4010  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4011  // avoid using AValue in denominator or have excessively long times
4012  {
4013  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4014  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4015  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4016 
4017  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4018  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4019  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4020  }
4021  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4022  // without the power need above condition or have hours of delay times, above added at v2.4.0
4023  {
4024  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4025  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4026  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4027  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4028  BrakeRate = 0;
4029  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4030  0.333334);
4031  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4032  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4034  // can accelerate continually over the half length
4035  {
4036  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4037  / 86400.0);
4039  // can accelerate continually over the full length
4040  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4041  {
4042  ExitTimeFull =
4043  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4044  }
4045  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4046  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4047  {
4048  // added at v0.6 as a safeguard
4049  if(MaxExitSpeed < EntrySpeed)
4050  {
4052  }
4053  // to prevent DeltaExitTimeToMaxInSecs being negative
4054  if(MaxExitSpeed < 1)
4055  {
4056  MaxExitSpeed = 1;
4057  }
4058  // to prevent divide by zero error
4059  // below as was
4061  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4062  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4063  (1.5 * AValue * AValue);
4064  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4065  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4066  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4067  }
4068  }
4069  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4070  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4071  // second halves of the element
4072  {
4073  // added at v0.6 as a safeguard
4074  if(MaxExitSpeed < EntrySpeed)
4075  {
4077  }
4078  // to prevent DeltaExitTimeToMaxInSecs being negative
4079  if(MaxExitSpeed < 1)
4080  {
4081  MaxExitSpeed = 1; // to prevent divide by zero error
4082  }
4083  // below as was
4085  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4086  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4087  (1.5 * AValue * AValue);
4088  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4089  // remaining distance to half length
4090  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4091  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4093  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4094  }
4095  }
4096  Utilities->CallLogPop(706);
4097  return;
4098  }
4099  else
4100  {
4101  if(!BuffersOrContinuationNowFlag)
4102  {
4103  if(NextSpeedLimit < LimitingSpeed)
4104  {
4105  LimitingSpeed = NextSpeedLimit;
4106  }
4107  }
4108  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4109  int TempMaxExitSpeed;
4110  // calc current value & if less than MaxExitSpeed set that to this
4111  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4112  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4113  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4114  {
4115  MaxExitSpeedAtHalfBraking = 0;
4116  }
4117  else
4118  {
4119  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4120  }
4121  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4122  {
4123  TempMaxExitSpeed = FrontElementMaxSpeed;
4124  }
4125  else
4126  {
4127  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4128  }
4129  if(TempMaxExitSpeed < MaxExitSpeed)
4130  {
4131  MaxExitSpeed = TempMaxExitSpeed;
4132  }
4133  // MaxExitSpeed is an external variable & this can reduce its value
4134  if(EntrySpeed > LimitingSpeed)
4135  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4136  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4137  {
4138  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4139  if(TempBrakeRate > MaxBrakeRate)
4140  {
4141  TempBrakeRate = MaxBrakeRate;
4142  }
4143  // shouldn't be for speedlimits since all known in advance, but include anyway
4144  if(TempBrakeRate > BrakeRate)
4145  {
4146  BrakeRate = TempBrakeRate;
4147  }
4148  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4149  }
4150  }
4151  if(!BuffersOrContinuationNowFlag)
4152  {
4153  CurrentTrackVectorPosition = NextTrackVectorPosition;
4154  EntryPos = NextEntryPos;
4155  CurrentElementHalfLength = NextElementHalfLength;
4156  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4157  {
4158  ContinuationNextFlag = true;
4159  }
4160  }
4161  }
4162  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4164  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4165  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4166  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4167  // stopping distance after it.
4168 
4169  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4170  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4171 
4172  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4173  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4174  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4175  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4176  // too late
4177 
4178  // set final braking or acc'n speed & time values
4179  if(BrakeRate > 0.01)
4180  {
4181  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4182  if(ExitSpeedHalfSquared < 10)
4183  {
4184  ExitSpeedHalf = 0;
4185  }
4186  else
4187  {
4188  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4189  }
4190  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4191  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4192  if(ExitSpeedFullSquared < 10)
4193  {
4194  ExitSpeedFull = 0;
4195  }
4196  else
4197  {
4198  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4199  }
4200  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4201  }
4202  else
4203  {
4204  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4205  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4206  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4207  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4208 
4209  BrakeRate = 0;
4210  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4211  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4212  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4214  {
4215  if(PowerAtRail > 1)
4216  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4217  {
4218  // [km/h/3.6 = m/s]
4219  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4220  / 86400.0);
4221  }
4222  else
4223  {
4224  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4225  }
4227  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4228  {
4229  if(PowerAtRail > 1)
4230  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4231  {
4232  // [km/h/3.6 = m/s]
4233  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4234  / 86400.0);
4235  }
4236  else
4237  {
4238  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4239  }
4240  }
4241  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4242  {
4243  // added at v0.6 as a safeguard
4244  if(MaxExitSpeed < EntrySpeed)
4245  {
4247  }
4248  // to prevent DeltaExitTimeToMaxInSecs being negative
4249  if(MaxExitSpeed < 1)
4250  {
4251  MaxExitSpeed = 1; // to prevent divide by zero error
4252  }
4253  // below as was
4255  double DeltaExitTimeToMaxInSecs;
4256  double DistanceToMax;
4257  if(PowerAtRail > 1) // added at v2.4.0
4258  {
4259  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4260  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4261  (1.5 * AValue * AValue);
4262  }
4263  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4264  {
4265  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4266  // these not really accurate but will be good enough
4267  DistanceToMax = EntryHalfLength;
4268  }
4269  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4270  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4271  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4272  }
4273  }
4274  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4275  {
4276  // added at v0.6 as a safeguard
4277  if(MaxExitSpeed < EntrySpeed)
4278  {
4280  }
4281  // to prevent DeltaExitTimeToMaxInSecs being negative
4282  if(MaxExitSpeed < 1)
4283  {
4284  MaxExitSpeed = 1; // to prevent divide by zero error
4285  }
4286  // below as was
4288  double DeltaExitTimeToMaxInSecs;
4289  double DistanceToMax;
4290  if(PowerAtRail > 1) // added at v2.4.0
4291  {
4292  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4293  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4294  (1.5 * AValue * AValue);
4295  }
4296  else
4297  {
4298  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4299  // these not really accurate but will be good enough
4300  DistanceToMax = EntryHalfLength / 2;
4301  }
4302  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4303  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4304  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4306  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4307  }
4308  }
4309  }
4310 
4311  else // SPADFlag set
4312  {
4314  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4315  if(ExitSpeedHalfSquared < 10)
4316  {
4317  ExitSpeedHalf = 0;
4318  }
4319  else
4320  {
4321  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4322  }
4323  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4324  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4325  if(ExitSpeedFullSquared < 10)
4326  {
4327  ExitSpeedFull = 0;
4328  }
4329  else
4330  {
4331  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4332  }
4333  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4334 
4335  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4336  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4337  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4338  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4339  // will be the stopping speed.
4340  if(ExitSpeedFull > 0)
4341  {
4342  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4343  {
4344  if((EntryPos == 0) || (EntryPos == 2))
4345  {
4346  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4347  {
4348  ExitPos = 1;
4349  }
4350  else
4351  {
4352  ExitPos = 3;
4353  }
4354  }
4355  else
4356  {
4357  ExitPos = 0;
4358  }
4359  }
4360  else
4361  {
4362  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4363  }
4364  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4365  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4366  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4367  {
4368  int NextElementLength;
4369  if(NextEntryPos > 1)
4370  {
4371  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4372  }
4373  else
4374  {
4375  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4376  }
4377  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4378  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4379  {
4380  ExitSpeedFull = 0;
4381  }
4382  }
4383  }
4384  }
4385  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4386  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4387  {
4388  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4389  if(LeadElement > -1)
4390  {
4392  // don't stop on a continuation either entering or leaving
4393  {
4396  MaxExitSpeed = LimitingSpeed;
4397  BrakeRate = 0;
4398  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4399  // assume length is 50m for ease of calc
4400  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4401  Utilities->CallLogPop(2126);
4402  return;
4403  }
4404  }
4405  else if(MidElement > -1)
4406  {
4408  {
4411  MaxExitSpeed = LimitingSpeed;
4412  BrakeRate = 0;
4413  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4414  // assume length is 50m for ease of calc
4415  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4416  Utilities->CallLogPop(2127);
4417  return;
4418  }
4419  }
4420  else if(LagElement > -1)
4421  {
4423  {
4426  MaxExitSpeed = LimitingSpeed;
4427  BrakeRate = 0;
4428  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4429  // assume length is 50m for ease of calc
4430  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4431  Utilities->CallLogPop(2128);
4432  return;
4433  }
4434  }
4435  if(EntrySpeed > 7.5) // keep going for at least another element
4436  {
4437  ExitSpeedHalf = EntrySpeed - 2.5;
4438  ExitSpeedFull = EntrySpeed - 5;
4439  MaxExitSpeed = LimitingSpeed;
4440  BrakeRate = 0;
4441  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4442  // assume length is 50m for ease of calc
4443  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4444  Utilities->CallLogPop(2129);
4445  return;
4446  }
4447  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4448  {
4449  // will appear to have slowed at steady rate
4450  ExitSpeedHalf = 0;
4451  ExitSpeedFull = 0;
4452  MaxExitSpeed = LimitingSpeed;
4453  BrakeRate = 0;
4454  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4455  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4456  StoppedWithoutPower = true;
4457  Utilities->CallLogPop(2130);
4458  return;
4459  }
4460  }
4461  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4462  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4463  Utilities->CallLogPop(707);
4464 }
4465 // ---------------------------------------------------------------------------
4466 /*
4467  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4468  {
4469  int NextExitPos;
4470  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4471  if(TimetableVector.empty()) return false;
4472  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4473  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4474  {
4475  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4476  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4477  {
4478  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4479  }
4480  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4481  {
4482  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4483  }
4484  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4485  NextElement = TempElement;
4486  }
4487  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4488  if(NextElement.TrackType == Buffers) return true;
4489  return false;//shouldn't reach here but include to prevent compiler return warning
4490  }
4491 */
4492 // ---------------------------------------------------------------------------
4493 
4494 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4495 /*
4496  returns the number by which the train ActionVectorEntryPtr needs
4497  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4498  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4499  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4500  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4501  or pass (false) the location.
4502 */{
4503  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4504  Stop = false;
4505  if(TimetableFinished || (Name == ""))
4506  {
4507  Utilities->CallLogPop(957);
4508  return(-1);
4509  }
4510  // start looking from current pointer position
4511  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4512  {
4513  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4514  {
4515  break;
4516  }
4517  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4518  {
4519  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4520  {
4521  Stop = true;
4522  Utilities->CallLogPop(960);
4523  return (Ptr - ActionVectorEntryPtr);
4524  }
4525  }
4526  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4527  {
4528  Utilities->CallLogPop(1517);
4529  return (Ptr - ActionVectorEntryPtr);
4530  }
4531  }
4532  Utilities->CallLogPop(959);
4533  return(-1); // not found a valid entry
4534 }
4535 
4536 // ---------------------------------------------------------------------------
4537 
4539 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4540  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4541  Ignores the call-on signal.
4542 */{
4543  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4544  int ReturnVal = 0;
4545 
4546  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4547  {
4548  Track->TrackVector.at(x).TempTrackMarker01 = false;
4549  Track->TrackVector.at(x).TempTrackMarker23 = false;
4550  }
4551  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4552  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4553 
4554  while(true)
4555  {
4556  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4557  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4558  {
4559  ReturnVal = 1;
4560  break;
4561  }
4562  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4563  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4564  {
4565  ReturnVal = 2;
4566  break;
4567  }
4568  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4569  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4570  {
4571  ReturnVal = 3;
4572  break;
4573  }
4574  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4575  CurrentTrackVectorPosition).TrackType == Crossover))
4576  {
4577  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4578  // must be a loop - reached same point as examined earlier
4579  {
4580  ReturnVal = 4;
4581  break;
4582  }
4583  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4584  {
4585  ReturnVal = 4;
4586  break;
4587  }
4588  }
4589  else
4590  {
4591  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4592  CurrentTrackVectorPosition).TempTrackMarker23))
4593  {
4594  ReturnVal = 4;
4595  break;
4596  }
4597  }
4598  if(EntryPos < 2)
4599  {
4600  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4601  }
4602  else
4603  {
4604  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4605  }
4606  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4607  {
4608  if((EntryPos == 0) || (EntryPos == 2))
4609  {
4610  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4611  {
4612  ExitPos = 1;
4613  }
4614  else
4615  {
4616  ExitPos = 3;
4617  }
4618  }
4619  else
4620  {
4621  ExitPos = 0;
4622  }
4623  }
4624  else
4625  {
4626  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4627  }
4628  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4629  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4630  CurrentTrackVectorPosition = NextTrackVectorPosition;
4631  EntryPos = NextEntryPos;
4632  }
4633  if(ReturnVal == 1)
4634  {
4635  Utilities->CallLogPop(708);
4636  return(false);
4637  }
4638  if(ReturnVal == 2)
4639  {
4640  Utilities->CallLogPop(709);
4641  return(true);
4642  }
4643  if(ReturnVal == 3)
4644  {
4645  Utilities->CallLogPop(946);
4646  return(true);
4647  }
4648  if(ReturnVal == 4)
4649  {
4650  Utilities->CallLogPop(947);
4651  return(true);
4652  }
4653  else
4654  {
4655  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4656  }
4657 }
4658 
4659 // ---------------------------------------------------------------------------
4660 
4662 /*
4663  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4664  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4665  change of direction (cdt), remaining here (Frh), or under signaller control);
4666  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4667  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4668  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4669  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4670  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4671  change points outside the route or have a route conflict if another route is set.
4672 */{
4673  if(Track->RouteFlashFlag)
4674  {
4675  return(false); // don't want to create a new route from the stop signal if one is already in construction as
4676  }
4677  // some of the callingon route elements may be involved
4678  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4679  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4680  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4681  int RouteStartPosition;
4682  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4683  int PlatformPosition;
4684  // the track vector position of the first stop platfrom
4685  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4686  // not used here
4687  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4688  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4689  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4690  // must be stopped at a signal but not at a location & still in timetable (a)
4692  // no need to check for SignallerStopped as this function only called in Timetable mode
4693  {
4694  Utilities->CallLogPop(711);
4695  return(false);
4696  }
4697  while(true)
4698  {
4699  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4700  // don't look further than 4km ahead (j)
4701  if(Distance > (4000 + LeadElementDistance))
4702  {
4703  Utilities->CallLogPop(967);
4704  return(false);
4705  }
4706  // if find another train on an element in front, before find a valid platform, return false (c)
4707  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4708  {
4709  Utilities->CallLogPop(713);
4710  return(false);
4711  }
4712  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4713  // be facing later on)
4714  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4715  {
4716  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4717  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4718  if(OtherTrain.LeadElement == -1)
4719  {
4720  Utilities->CallLogPop(714);
4721  return(false);
4722  }
4723  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4724  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4725  {
4726  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4727  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4728  (OtherTrain.TrainMode == Signaller))
4729  {
4730  break;
4731  }
4732  else
4733  {
4734  Utilities->CallLogPop(955);
4735  return(false);
4736  }
4737  }
4738  else // (h)
4739  {
4740  break;
4741  }
4742  }
4743  // if reach buffers or exit continuation return false (can set route)
4744  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4745  {
4746  Utilities->CallLogPop(716);
4747  return(false);
4748  }
4749  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4750  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4752  {
4753  Utilities->CallLogPop(717);
4754  return(false);
4755  }
4756  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4757 /*
4758  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4759  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4760  {
4761  Utilities->CallLogPop(718);
4762  return false;
4763  }
4764 */
4765  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4766  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4767  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4768  {
4769  if(StopRequired)
4770  {
4771  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4772  {
4773  if(!PlatformFoundFlag)
4774  {
4775  PlatformPosition = CurrentTrackVectorPosition;
4776  }
4777  // ensure this only set once at first valid platform position - the unrestricted route will end here
4778  PlatformFoundFlag = true;
4779  }
4780  }
4781  }
4782  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4783  // train has to be at station but that has to be before the next forward signal
4784 /*
4785  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4786  {
4787  Utilities->CallLogPop(719);
4788  return false;
4789  }
4790 */
4791  // make sure points are followed correctly (d) & set ExitPos
4792  if(CurrentTrackElement.TrackType == Points)
4793  {
4794  if((EntryPos == 0) || (EntryPos == 2))
4795  {
4796  if(CurrentTrackElement.Attribute == 0)
4797  {
4798  ExitPos = 1;
4799  }
4800  else
4801  {
4802  ExitPos = 3;
4803  }
4804  }
4805  if(EntryPos == 1)
4806  {
4807  if(CurrentTrackElement.Attribute == 0)
4808  {
4809  ExitPos = 0;
4810  }
4811  else
4812  {
4813  Utilities->CallLogPop(720);
4814  return(false);
4815  }
4816  }
4817  if(EntryPos == 3)
4818  {
4819  if(CurrentTrackElement.Attribute == 1)
4820  {
4821  ExitPos = 0;
4822  }
4823  else
4824  {
4825  Utilities->CallLogPop(721);
4826  return(false);
4827  }
4828  }
4829  }
4830  else
4831  {
4832  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4833  }
4834  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4835  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4836  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4837  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4838  if(ElementNumber < 2)
4839  {
4840  SkipRouteCheck = true;
4841  }
4842  else
4843  {
4844  SkipRouteCheck = false;
4845  }
4846  if(ElementNumber == 1) // the stop signal
4847  {
4848  RouteStartPosition = CurrentTrackVectorPosition;
4849  }
4850 /*
4851  if(ElementNumber == 2)
4852  {
4853  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4854  else AutoSigs = false;
4855  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4856  }
4857 */
4858  if(ElementNumber > 1)
4859  {
4860  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4861  {
4862  RouteOrPartRouteSet = true;
4863  }
4864  else
4865  {
4866  RouteOrPartRouteSet = false;
4867  }
4868  }
4869  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4870  {
4871  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4872  {
4873  Utilities->CallLogPop(1859);
4874  return(false);
4875  }
4876  int ExitLink = CurrentTrackElement.Link[ExitPos];
4877  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4878  {
4879  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4880  {
4881  Utilities->CallLogPop(1850);
4882  return(false);
4883  }
4884  }
4885  }
4886  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4887  if(EntryPos < 2)
4888  {
4889  Distance += CurrentTrackElement.Length01;
4890  }
4891  else
4892  {
4893  Distance += CurrentTrackElement.Length23;
4894  }
4895  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4896  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4897  CurrentTrackVectorPosition = NextTrackVectorPosition;
4898  EntryPos = NextEntryPos;
4899  ElementNumber++;
4900  } // while(true)
4901 
4902  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4903  // from the stop signal (note that it may be last in an autosigs route)
4904  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4905  // this in ClockTimer2)
4906 
4907  // now add elements to the CallonVector
4908  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4909 
4910  AllRoutes->CallonVector.push_back(CallonEntry);
4911  Utilities->CallLogPop(1860);
4912  return(true); // return false if fail to set route for any reason
4913 }
4914 
4915 // ---------------------------------------------------------------------------
4916 /*
4917  bool TTrain::TimetableFinished()
4918  {
4919  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4920  {
4921  return true;
4922  }
4923  return false;
4924  }
4925 */
4926 // ---------------------------------------------------------------------------
4927 
4928 AnsiString TTrain::GetTrainHeadCode(int Caller)
4929 
4930 {
4931  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4932  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4933 
4934  Utilities->CallLogPop(1452);
4935  return(RepeatHeadCode);
4936 }
4937 
4938 // ---------------------------------------------------------------------------
4939 
4940 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4941 {
4942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4943  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4944 
4945  Utilities->CallLogPop(1453);
4946  return(RepeatTime);
4947 }
4948 
4949 // ---------------------------------------------------------------------------
4950 
4951 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4952 {
4953  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4955  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4956  // first check that train is fully on the railway
4957  bool FrontValid = false, RearValid = false;
4958  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4959 
4960  if((LeadElement == -1) || (MidElement == -1))
4961  {
4962  TrainToBeJoinedBy = NULL;
4963  Utilities->CallLogPop(2131);
4964  return(false);
4965  }
4967  {
4968  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4969  FrontValid = true;
4970  }
4972  {
4973  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4974  RearValid = true;
4975  }
4976  int TrainToBeJoinedByID = -1;
4977 
4978  // first check if on a 2-track element & select correct ID number if so
4979  if(FrontValid)
4980  {
4981  if(FrontAdjacentTrackElement.TrackType == Bridge)
4982  {
4984  {
4985  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4986  }
4987  else
4988  {
4989  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4990  }
4991  }
4992  else
4993  {
4994  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4995  }
4996  }
4997  if((TrainToBeJoinedByID < 0) && RearValid)
4998  {
4999  // first check if on a 2-track element & select correct ID number if so
5000  if(RearAdjacentTrackElement.TrackType == Bridge)
5001  {
5003  {
5004  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5005  }
5006  else
5007  {
5008  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5009  }
5010  }
5011  else
5012  {
5013  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5014  }
5015  }
5016  if(TrainToBeJoinedByID < 0) // no adjacent train
5017  {
5018  TrainToBeJoinedBy = NULL;
5019  Utilities->CallLogPop(2132);
5020  return(false);
5021  }
5022  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5023  if(!TrainToBeJoinedBy->Stopped())
5024  {
5025  TrainToBeJoinedBy = NULL;
5026  Utilities->CallLogPop(2133);
5027  return(false);
5028  }
5029  Utilities->CallLogPop(2134);
5030  return(true);
5031 }
5032 
5033 // ---------------------------------------------------------------------------
5034 
5035 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5036  TDateTime TimetableNonRepeatTime, bool Warning)
5037 /*
5038  Time = timetable time, the time adjustments for repeat trains is carried out internally
5039  Not all messages need this, if not needed a dummy value is required but not used
5040 
5041  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5042  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5043  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5044  //NB for Frh just give terminated message but without event time - don't use this function
5045  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5046  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5047  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5048  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5049  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5050  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5051  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5052  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5053  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5054  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5055  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5056  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5057  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5058  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5059  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5060  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5061  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5062  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5063  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5064  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5065  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5066  SignallerStop 06:05:40: 2F46 stopped on signaller command
5067  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5068  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5069 */{
5070  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5071  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5072  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5073  int IntMinsLate = 0;
5074 
5075  // need to set it in case MinsLate == 0, since it isn't tested for that
5076  if(ActionType == Arrive)
5077  {
5078  ActionLog = " arrived at ";
5079  }
5080  if(ActionType == Terminate)
5081  {
5082  if(TerminatedMessageSent) // to avoid it being sent twice
5083  {
5084  Utilities->CallLogPop(1104);
5085  return;
5086  }
5087  ActionLog = " terminated at ";
5088  TerminatedMessageSent = true;
5089  }
5090  if(ActionType == Depart)
5091  {
5092  ActionLog = " departed from ";
5093  }
5094  if(ActionType == Pass)
5095  {
5096  ActionLog = " passed ";
5097  }
5098  if(ActionType == Create)
5099  {
5100  ActionLog = " created at ";
5101  }
5102  if(ActionType == Enter)
5103  {
5104  ActionLog = " entered railway at ";
5105  }
5106  if(ActionType == Leave)
5107  {
5108  ActionLog = " left railway at ";
5109  }
5110  if(ActionType == FrontSplit)
5111  {
5112  ActionLog = " split from front to ";
5113  }
5114  if(ActionType == RearSplit)
5115  {
5116  ActionLog = " split from rear to ";
5117  }
5118  if(ActionType == JoinedByOther)
5119  {
5120  ActionLog = " joined by ";
5121  }
5122  if(ActionType == ChangeDirection)
5123  {
5124  ActionLog = " changed direction at ";
5125  }
5126  if(ActionType == NewService)
5127  {
5128  ActionLog = " became new service ";
5129  }
5130  if(ActionType == TakeSignallerControl)
5131  {
5132  ActionLog = " taken under signaller control at ";
5133  }
5134  if(ActionType == RestoreTimetableControl)
5135  {
5136  ActionLog = " restored to timetable control at ";
5137  }
5138  if(ActionType == RemoveTrain)
5139  {
5140  if(Crashed)
5141  {
5142  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5143  }
5144  else if(Derailed)
5145  {
5146  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5147  }
5148  else
5149  {
5150  ActionLog = " REMOVED FROM RAILWAY at ";
5151  }
5152  }
5153  if(ActionType == SignallerMoveForwards)
5154  {
5155  ActionLog = " received signaller authority to proceed";
5156  }
5157  if(ActionType == SignallerStepForward)
5158  {
5159  ActionLog = " received signaller authority to step forward";
5160  }
5161  if(ActionType == SignallerChangeDirection)
5162  {
5163  ActionLog = " changed direction under signaller control at ";
5164  }
5165  if(ActionType == SignallerPassRedSignal)
5166  {
5167  ActionLog = " received signaller authority to pass red signal";
5168  }
5169  if(ActionType == SignallerControlStop)
5170  {
5171  ActionLog = " received signaller instruction to stop";
5172  }
5173  if(ActionType == SignallerStop)
5174  {
5175  ActionLog = " stopped on signaller instruction ";
5176  }
5177  if(ActionType == SignallerJoin)
5178  {
5179  ActionLog = " joined under signaller control by ";
5180  }
5181  if(ActionType == TrainFailure)
5182  {
5183  ActionLog = " suffered an onboard power failure at ";
5184  }
5185  if(ActionType == RepairFailedTrain)
5186  {
5187  ActionLog = " failure repaired at ";
5188  }
5189  if(ActionType == SignallerLeave)
5190  {
5191  ActionLog = " left railway under signaller control at ";
5192  }
5193  if(OtherHeadCode != "")
5194  {
5195  OtherHeadCode += " at ";
5196  }
5197  TDateTime ActualTime = TrainController->TTClockTime;
5198 
5199  if(Warning)
5200  {
5201  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5202  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5203  }
5204  else
5205  {
5206  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5207  }
5208  bool TimePerformance = true;
5209 
5210  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5211  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5212  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5213  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5214  // SignallerJoin & RepairFailedTrain new at v2.4.0
5215  {
5216  TimePerformance = false;
5217  }
5218  if(TimePerformance)
5219  {
5220  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5221  MinsDelayed = float(MinsLate);
5222  // new v2.2.0 for OpActionPanel, can be positive or negative
5223  if(ActionType == Arrive)
5224  {
5226  }
5227  // since train has just arrived this value is the
5228  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5229  // subtracted from later stop recoverable times.
5230  if(MinsLate < 0)
5231  {
5232  IntMinsLate = int(ceil(MinsLate));
5233  }
5234  if(MinsLate > 0)
5235  {
5236  IntMinsLate = int(floor(MinsLate));
5237  }
5238  if(IntMinsLate == 0)
5239  {
5240  PerfLog = " on time";
5241  }
5242  else if(IntMinsLate == 1)
5243  {
5244  PerfLog = " 1 minute late";
5245  }
5246  else if(IntMinsLate == -1)
5247  {
5248  PerfLog = " 1 minute early";
5249  }
5250  else if(IntMinsLate > 1)
5251  {
5252  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5253  }
5254  else if(IntMinsLate < -1)
5255  {
5256  int PosIntMinsLate = -IntMinsLate;
5257  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5258  }
5259  if(LocationName.Pos('-') > 0)
5260  {
5261  PerfLog = "," + PerfLog;
5262  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5263  }
5264  Display->PerformanceLog(0, BaseLog + PerfLog);
5265  }
5266  else
5267  {
5268  Display->PerformanceLog(1, BaseLog);
5269  }
5270  if(Warning)
5271  {
5272  Display->WarningLog(0, WarningBaseLog);
5273  }
5274  // update statistics
5275  if((ActionType == Arrive) && (IntMinsLate == 0))
5276  {
5278  }
5279  else if((ActionType == Arrive) && (IntMinsLate > 0))
5280  {
5282  TrainController->TotLateArrMins += IntMinsLate;
5283  }
5284  else if((ActionType == Arrive) && (IntMinsLate < 0))
5285  {
5287  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5288  }
5289  else if((ActionType == Pass) && (IntMinsLate == 0))
5290  {
5292  }
5293  else if((ActionType == Pass) && (IntMinsLate > 0))
5294  {
5296  TrainController->TotLatePassMins += IntMinsLate;
5297  }
5298  else if((ActionType == Pass) && (IntMinsLate < 0))
5299  {
5301  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5302  }
5303  else if((ActionType == Depart) && (IntMinsLate == 0))
5304  {
5306  }
5307  else if((ActionType == Depart) && (IntMinsLate > 0))
5308  {
5310  TrainController->TotLateDepMins += IntMinsLate;
5311  }
5312  Utilities->CallLogPop(968);
5313 }
5314 
5315 // ---------------------------------------------------------------------------
5316 
5317 void TTrain::TrainHasFailed(int Caller)
5318 {
5319  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5320  if(Crashed || Derailed || DerailPending)
5321  {
5322  TrainFailurePending = false;
5323  Utilities->CallLogPop(2135);
5324  return;
5325  }
5326  AnsiString LocName = "";
5327 
5328  if(LeadElement > -1)
5329  {
5331  }
5332  if((LocName == "") && (MidElement > -1))
5333  {
5335  }
5336  if((LocName == "") && LeadElement > -1)
5337  {
5338  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5339  }
5340  if((LocName == "") && (MidElement > -1))
5341  {
5342  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5343  }
5344  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5345  TrainFailed = true;
5346  TrainFailurePending = false;
5348  PowerAtRail = 0.08;
5349  AValue = sqrt(2 * PowerAtRail / Mass);
5351  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5352  if(Stopped())
5353  {
5354  EntrySpeed = 0;
5355  ExitSpeedHalf = 0;
5356  ExitSpeedFull = 0;
5357  MaxExitSpeed = 0;
5358  BrakeRate = 0;
5359  StoppedWithoutPower = true;
5360  }
5362  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5363  // true for warning, TDateTime not used
5364  Utilities->CallLogPop(2136);
5365 }
5366 
5367 // ---------------------------------------------------------------------------
5368 
5369 void TTrain::FrontTrainSplit(int Caller)
5370 {
5371 /*
5372  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5373  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5374  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5375  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5376 */
5377  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5378  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5379  if(PowerAtRail < 1)
5380  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5381  {
5383  {
5384  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5385  }
5387  Utilities->CallLogPop(2137);
5388  return;
5389  }
5390  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5391 
5392  if(LocationName == "")
5393  {
5394  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5395  }
5396  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5397  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5398  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5400 
5401  // determine all positions & exits
5402  if(LocationName != "")
5403  {
5404  // if message given only call at ~5 sec intervals
5406  {
5407  FirstNamedElementPos = LeadElement;
5408  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5409  // check if possible with LeadElement as First
5410  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5411  {
5412  FirstNamedElementPos = MidElement;
5413  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5414  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5415  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5416  {
5418  {
5419  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5421  }
5422  Utilities->CallLogPop(1009);
5423  return;
5424  }
5425  }
5426  else
5427  {
5428  // if first is possible then check if all 4 positions at location, and if not try the second
5429  int LeadPosA = FirstNamedElementPos;
5430  int LeadPosB = FirstNamedLinkedElementPos;
5431  int LeadPosC = SecondNamedElementPos;
5432  int LeadPosD = SecondNamedLinkedElementPos;
5433  // count number of positions that are at the location
5434  int LeadNumAtLoc = 0;
5435  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5436  {
5437  LeadNumAtLoc++;
5438  }
5439  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5440  {
5441  LeadNumAtLoc++;
5442  }
5443  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5444  {
5445  LeadNumAtLoc++;
5446  }
5447  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5448  {
5449  LeadNumAtLoc++;
5450  }
5451  if(LeadNumAtLoc < 4)
5452  {
5453  FirstNamedElementPos = MidElement;
5454  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5455  SecondNamedLinkedElementPos)) // restore originals
5456  {
5457  FirstNamedElementPos = LeadPosA;
5458  FirstNamedLinkedElementPos = LeadPosB;
5459  SecondNamedElementPos = LeadPosC;
5460  SecondNamedLinkedElementPos = LeadPosD;
5461  }
5462  else // count number at location
5463  {
5464  int MidNumAtLoc = 0;
5465  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5466  {
5467  MidNumAtLoc++;
5468  }
5469  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5470  {
5471  MidNumAtLoc++;
5472  }
5473  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5474  {
5475  MidNumAtLoc++;
5476  }
5477  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5478  {
5479  MidNumAtLoc++;
5480  }
5481  if(LeadNumAtLoc > MidNumAtLoc)
5482  // change back, else keep new values
5483  {
5484  FirstNamedElementPos = LeadPosA;
5485  FirstNamedLinkedElementPos = LeadPosB;
5486  SecondNamedElementPos = LeadPosC;
5487  SecondNamedLinkedElementPos = LeadPosD;
5488  }
5489  }
5490  }
5491  }
5492  }
5493  else
5494  {
5495  Utilities->CallLogPop(1791);
5496  return;
5497  }
5498  }
5499  else
5500  {
5501  throw Exception("Error - LocationName not set in FrontTrainSplit");
5502  }
5503  // set front & rear train parameters
5504  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5505  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5506  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5507  if(LeadElement == FirstNamedElementPos)
5508  {
5509  if(MidElement == SecondNamedElementPos)
5510  {
5511  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5512  FrontTrainRearPosition = FirstNamedElementPos;
5513  RearTrainFrontPosition = SecondNamedElementPos;
5514  RearTrainRearPosition = SecondNamedLinkedElementPos;
5515  }
5516  else // MidElement must == FirstNamedLinkedElementPos
5517  {
5518  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5519  FrontTrainRearPosition = SecondNamedElementPos;
5520  RearTrainFrontPosition = FirstNamedElementPos;
5521  RearTrainRearPosition = FirstNamedLinkedElementPos;
5522  }
5523  }
5524  else // MidElement == FirstNamedElementPos
5525  {
5526  if(LeadElement == SecondNamedElementPos)
5527  {
5528  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5529  FrontTrainRearPosition = SecondNamedElementPos;
5530  RearTrainFrontPosition = FirstNamedElementPos;
5531  RearTrainRearPosition = FirstNamedLinkedElementPos;
5532  }
5533  else // LeadElement must == FirstNamedLinkedElementPos
5534  {
5535  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5536  FrontTrainRearPosition = FirstNamedElementPos;
5537  RearTrainFrontPosition = SecondNamedElementPos;
5538  RearTrainRearPosition = SecondNamedLinkedElementPos;
5539  }
5540  }
5541  RearTrainExitPos = -1;
5542  for(int x = 0; x < 4; x++)
5543  {
5544  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5545  {
5546  RearTrainExitPos = x;
5547  break;
5548  }
5549  }
5550  if(RearTrainExitPos == -1)
5551  {
5552  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5553  }
5554  FrontTrainExitPos = -1;
5555  for(int x = 0; x < 4; x++)
5556  {
5557  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5558  {
5559  FrontTrainExitPos = x;
5560  break;
5561  }
5562  }
5563  if(FrontTrainExitPos == -1)
5564  {
5565  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5566  }
5567  // check no train (apart from self) on any of the 4 elements & fail if so
5568  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5569  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5570 
5571  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5572  {
5573  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5574  }
5575  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5576  {
5577  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5578  }
5579  else
5580  {
5581  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5582  }
5583  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5584  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5585  // can't be a bridge
5586  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5587  // can't be a bridge
5588  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5589  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5590 
5591  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5592  {
5593  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5594  }
5595  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5596  {
5597  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5598  }
5599  else
5600  {
5601  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5602  }
5603  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5604  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5605  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5606  {
5608  {
5611  }
5612  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5613  Utilities->CallLogPop(1010);
5614  return;
5615  }
5617  {
5619  }
5620  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5621  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5622  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5623  // variable as it is needed for setting up the new train
5624  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5625 
5626  UnplotTrain(0);
5627  StartSpeed = 0;
5628  RearStartElement = RearTrainRearPosition;
5629  RearStartExitPos = RearTrainExitPos;
5630  StoppedAtLocation = true;
5631  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5632  {
5633  StoppedWithoutPower = true;
5634  }
5635  PlotStartPosition(3);
5640 
5641  Mass = Mass / 2;
5642  MaxBrakeRate = MaxBrakeRate / 2;
5643  PowerAtRail = PowerAtRail / 2;
5644  AValue = sqrt(2 * PowerAtRail / Mass);
5645  // shouldn't change but include in case not set earlier
5646 
5647  // create new front train
5648 /*
5649  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5650  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5651  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5652 */
5653  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5654  TActionEventType EventType = NoEvent;
5655 
5656  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5657  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5658  // false for SignallerControl
5659  {
5660  Utilities->CallLogPop(1721); // EventType not used here
5661  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5662  // another train, in which case a message will have been sent to the perf log, also might well clear later
5663  // when other train moves away
5664  return;
5665  }
5666  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5667  // see mods in UpdateTrain for v1.3.2
5668  TrainController->TrainAdded = true;
5669 
5670  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5671 
5672  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5673  TTOD.RunningEntry = Running;
5674  Utilities->CallLogPop(998);
5675 }
5676 
5677 // ---------------------------------------------------------------------------
5678 
5679 void TTrain::RearTrainSplit(int Caller)
5680 {
5681 /*
5682  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5683  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5684  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5685  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5686 */
5687  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5688  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5689  if(PowerAtRail < 1)
5690  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5691  {
5693  {
5694  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5695  }
5697  Utilities->CallLogPop(2138);
5698  return;
5699  }
5700  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5701 
5702  if(LocationName == "")
5703  {
5704  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5705  }
5706  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5707  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5708  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5710 
5711  // determine all positions & exits
5712  if(LocationName != "")
5713  {
5714  // if message given only call at ~5 sec intervals
5716  {
5717  FirstNamedElementPos = LeadElement;
5718  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5719  SecondNamedLinkedElementPos))
5720  {
5721  FirstNamedElementPos = MidElement;
5722  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5723  SecondNamedLinkedElementPos))
5724  {
5726  {
5727  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5729  }
5730  Utilities->CallLogPop(1013);
5731  return;
5732  }
5733  }
5734  else
5735  {
5736  // if first is possible then check if all 4 positions at location, and if not try the second
5737  int LeadPosA = FirstNamedElementPos;
5738  int LeadPosB = FirstNamedLinkedElementPos;
5739  int LeadPosC = SecondNamedElementPos;
5740  int LeadPosD = SecondNamedLinkedElementPos;
5741  // count number of positions that are at the location
5742  int LeadNumAtLoc = 0;
5743  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5744  {
5745  LeadNumAtLoc++;
5746  }
5747  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5748  {
5749  LeadNumAtLoc++;
5750  }
5751  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5752  {
5753  LeadNumAtLoc++;
5754  }
5755  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5756  {
5757  LeadNumAtLoc++;
5758  }
5759  if(LeadNumAtLoc < 4)
5760  {
5761  FirstNamedElementPos = MidElement;
5762  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5763  SecondNamedLinkedElementPos)) // restore originals
5764  {
5765  FirstNamedElementPos = LeadPosA;
5766  FirstNamedLinkedElementPos = LeadPosB;
5767  SecondNamedElementPos = LeadPosC;
5768  SecondNamedLinkedElementPos = LeadPosD;
5769  }
5770  else // count number at location
5771  {
5772  int MidNumAtLoc = 0;
5773  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5774  {
5775  MidNumAtLoc++;
5776  }
5777  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5778  {
5779  MidNumAtLoc++;
5780  }
5781  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5782  {
5783  MidNumAtLoc++;
5784  }
5785  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5786  {
5787  MidNumAtLoc++;
5788  }
5789  if(LeadNumAtLoc > MidNumAtLoc)
5790  // change back, else keep new values
5791  {
5792  FirstNamedElementPos = LeadPosA;
5793  FirstNamedLinkedElementPos = LeadPosB;
5794  SecondNamedElementPos = LeadPosC;
5795  SecondNamedLinkedElementPos = LeadPosD;
5796  }
5797  }
5798  }
5799  }
5800  }
5801  else
5802  {
5803  Utilities->CallLogPop(1792);
5804  return;
5805  }
5806  }
5807  else
5808  {
5809  throw Exception("Error - LocationName not set in RearTrainSplit");
5810  }
5811  // set front & rear train parameters
5812  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5813  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5814  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5815  if(LeadElement == FirstNamedElementPos)
5816  {
5817  if(MidElement == SecondNamedElementPos)
5818  {
5819  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5820  FrontTrainRearPosition = FirstNamedElementPos;
5821  RearTrainFrontPosition = SecondNamedElementPos;
5822  RearTrainRearPosition = SecondNamedLinkedElementPos;
5823  }
5824  else // MidElement must == FirstNamedLinkedElementPos
5825  {
5826  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5827  FrontTrainRearPosition = SecondNamedElementPos;
5828  RearTrainFrontPosition = FirstNamedElementPos;
5829  RearTrainRearPosition = FirstNamedLinkedElementPos;
5830  }
5831  }
5832  else // MidElement == FirstNamedElementPos
5833  {
5834  if(LeadElement == SecondNamedElementPos)
5835  {
5836  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5837  FrontTrainRearPosition = SecondNamedElementPos;
5838  RearTrainFrontPosition = FirstNamedElementPos;
5839  RearTrainRearPosition = FirstNamedLinkedElementPos;
5840  }
5841  else // LeadElement must == FirstNamedLinkedElementPos
5842  {
5843  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5844  FrontTrainRearPosition = FirstNamedElementPos;
5845  RearTrainFrontPosition = SecondNamedElementPos;
5846  RearTrainRearPosition = SecondNamedLinkedElementPos;
5847  }
5848  }
5849  RearTrainExitPos = -1;
5850  for(int x = 0; x < 4; x++)
5851  {
5852  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5853  {
5854  RearTrainExitPos = x;
5855  break;
5856  }
5857  }
5858  if(RearTrainExitPos == -1)
5859  {
5860  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5861  }
5862  FrontTrainExitPos = -1;
5863  for(int x = 0; x < 4; x++)
5864  {
5865  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5866  {
5867  FrontTrainExitPos = x;
5868  break;
5869  }
5870  }
5871  if(FrontTrainExitPos == -1)
5872  {
5873  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5874  }
5875  // check no train (apart from self) on any of the 4 elements & fail if so
5876  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5877  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5878 
5879  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5880  {
5881  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5882  }
5883  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5884  {
5885  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5886  }
5887  else
5888  {
5889  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5890  }
5891  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5892  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5893  // can't be a bridge
5894  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5895  // can't be a bridge
5896  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5897  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5898 
5899  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5900  {
5901  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5902  }
5903  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5904  {
5905  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5906  }
5907  else
5908  {
5909  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5910  }
5911  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5912  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5913  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5914  {
5916  {
5919  }
5920  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5921  Utilities->CallLogPop(1014);
5922  return;
5923  }
5925  {
5927  }
5928  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5929  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5930  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5931  // variable as it is needed for setting up the new train
5932  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5933 
5934  UnplotTrain(1);
5935  StartSpeed = 0;
5936  RearStartElement = FrontTrainRearPosition;
5937  RearStartExitPos = FrontTrainExitPos;
5938  StoppedAtLocation = true;
5939  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5940  {
5941  StoppedWithoutPower = true;
5942  }
5943  PlotStartPosition(4);
5948  Mass = Mass / 2;
5949  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5950  MaxBrakeRate = MaxBrakeRate / 2;
5951  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5952  PowerAtRail = PowerAtRail / 2;
5953  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5954  AValue = sqrt(2 * PowerAtRail / Mass);
5955  // shouldn't change but include in case not set earlier
5956 
5957  // create new rear train
5958 /*
5959  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5960  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5961  int RepeatNumber, int IncrementalMinutes)
5962 */
5963  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5964  TActionEventType EventType = NoEvent;
5965 
5966  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5967  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5968  // false for SignallerControl
5969  {
5970  Utilities->CallLogPop(1722); // EventType not used here
5971  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5972  // another train, in which case a message will have been sent to the perf log, also might well clear later
5973  // when other train moves away
5974  return;
5975  }
5976  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5977  // see mods in UpdateTrain for v1.3.2
5978  TrainController->TrainAdded = true;
5979  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5980 
5981  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5982  TTOD.RunningEntry = Running;
5983  Utilities->CallLogPop(1015);
5984 }
5985 
5986 // ---------------------------------------------------------------------------
5987 
5988 void TTrain::FinishJoin(int Caller)
5989 {
5990  if(FinishJoinLogSent == false)
5991  {
5992  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5993  FinishJoinLogSent = true; // so don't keep logging this event
5994  // don't need to reset it to false after the event as the train is deleted
5995  }
5996  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5997  if(TrainFailed)
5998  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
5999  {
6001  {
6002  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6003  }
6005  Utilities->CallLogPop(2139);
6006  return;
6007  }
6008  if(TrainGone)
6009  // this means that the train has already joined the other & is awaiting deletion by TrainController
6010  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6011  // on from jbo & TrainToJoinIsAdjacent returns false
6012  {
6013  Utilities->CallLogPop(1035);
6014  return;
6015  }
6016  TTrain *TrainToJoin;
6018 
6019  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6020  {
6022  {
6023  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6026  }
6027  Utilities->CallLogPop(1030);
6028  return; // keep this here in case need to add code before final return
6029  }
6030  // no need to clear error report flag here, cleared in jbo function
6031  // No need to set TimetableFinished, done in jbo function
6032  Utilities->CallLogPop(1031);
6033 }
6034 
6035 // ---------------------------------------------------------------------------
6036 
6037 void TTrain::JoinedBy(int Caller)
6038 {
6039  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6040  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6041  if(PowerAtRail < 1)
6042  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6043  {
6045  {
6046  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6047  }
6049  Utilities->CallLogPop(2140);
6050  return;
6051  }
6052  TTrain *TrainToBeJoinedBy;
6054 
6055  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6056  {
6058  {
6059  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6062  }
6063  LastActionDelayFlag = true;
6064  // need to update LastActionTime if this train first to arrive as need 30s after
6065  // both adjacent before the join
6066  Utilities->CallLogPop(1032);
6067  return;
6068  }
6069  // here when other train is adjacent
6071  {
6073  // need to update this as need 30s after both adjacent before the join
6074  LastActionDelayFlag = false;
6075  Utilities->CallLogPop(1033);
6076  return;
6077  }
6078  // here when other train is adjacent & 30 secs elapsed since both adjacent
6079 
6080  // set new values for mass etc
6081  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6082  {
6083  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6084  }
6085  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6086  double OwnBrakeForce = MaxBrakeRate * Mass;
6087  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6088 
6089  Mass += TrainToBeJoinedBy->Mass;
6090  MaxBrakeRate = CombinedBrakeRate;
6091  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6092  AValue = sqrt(2 * PowerAtRail / Mass);
6093 
6095  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6096  TrainToBeJoinedBy->TimetableFinished = true;
6097  TrainToBeJoinedBy->TrainGone = true;
6098  // this will cause other train to be deleted
6099  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6103  Utilities->CallLogPop(1034);
6104 }
6105 
6106 // ---------------------------------------------------------------------------
6107 
6109 {
6110  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6111  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6112  if(PowerAtRail < 1)
6113  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6114  {
6116  {
6117  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6118  }
6119  ZeroPowerNoCDTMessage = true;
6120  Utilities->CallLogPop(2141);
6121  return;
6122  }
6123  TColor TempColour = BackgroundColour;
6124 
6125  UnplotTrain(2);
6128  StartSpeed = 0;
6129  StoppedAtLocation = true;
6130  PlotStartPosition(1);
6131  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6132  // plot same as was - should always be pale green
6136 
6137  //now erase a stub route if there is one, added at v2.5.1
6138  //first element of route is now immediately behind the train (i.e. next to MidElement)
6139  if(MidEntryPos >= 0)
6140  {
6141  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6142  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6143  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6144  int RouteNumber = -1;
6145  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6146  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6147  {
6148  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6149  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6150  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6151  {
6152  bool FirstPass = true; //added at v2.8.0
6153  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6154  {
6155  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6156  int TVPos2 = PDE.GetTrackVectorPosition();
6157  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6158  {
6159  break;
6160  }
6161  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6163  {
6164  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6165  }
6166  else
6167  {
6168  break;
6169  }
6170  FirstPass = false;
6171  }
6172  AllRoutes->RebuildRailwayFlag = true;
6173  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6174  }
6175  }
6176  }
6177  Utilities->CallLogPop(1012);
6178 }
6179 
6180 // ---------------------------------------------------------------------------
6181 
6182 void TTrain::NewTrainService(int Caller)
6183 // change to new train, give new service message
6184 {
6185  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6186  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6187  if(PowerAtRail < 1)
6188  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6189  {
6191  {
6192  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6193  }
6195  Utilities->CallLogPop(2142);
6196  return;
6197  }
6199 
6201  UnplotTrain(3);
6204  StartSpeed = 0;
6209  HeadCode = NewHeadCode;
6210  StoppedAtLocation = true;
6211  PlotStartPosition(5);
6213  // pale green
6216  TerminatedMessageSent = false;
6217  Utilities->CallLogPop(1022);
6218 }
6219 
6220 // ---------------------------------------------------------------------------
6221 
6222 void TTrain::RemainHere(int Caller)
6223 {
6224  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6225  if(RemainHereLogNotSent) // to prevent repeated logs
6226  {
6227  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6228  RemainHereLogNotSent = false;
6229  }
6231  {
6232  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6235  TerminatedMessageSent = true;
6236  }
6237  TimetableFinished = true;
6238  Utilities->CallLogPop(1023);
6239 }
6240 
6241 // ---------------------------------------------------------------------------
6242 
6243 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6244 /*
6245  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6246  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6247  except where an action is a departure, starting at the current value for the pointer
6248  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6249  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6250 */{
6251  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6252  {
6253  return; // if remove train that starts under signaller control no messages needed
6254 
6255  }
6256  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6257  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6258  if(IncNum > 0)
6259  {
6260  for(int x = 0; x < IncNum; x++)
6261  {
6262  if(x > 0)
6263  {
6264  Ptr++;
6265  }
6266  // arrival - no need to test for termination as this section only covers missed actions up to the
6267  // arrival point - may terminate later but that not missed
6268  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6269  {
6271  }
6272  // arrival & departure
6273  if(Ptr->FormatType == TimeTimeLoc)
6274  {
6276  }
6277  // departure
6278  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6279  {
6280  continue; // skip TimeLoc departures, message given for arrivals
6281  }
6282  // pass
6283  else if(Ptr->FormatType == PassTime)
6284  {
6286  }
6287  // split
6288  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6289  {
6291  }
6292  // jbo
6293  else if(Ptr->Command == "jbo")
6294  {
6296  }
6297  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6298  // be starts, finishes or cdt
6299  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6300  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6301  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6302  (Ptr->FormatType == Repeat))
6303  {
6304  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6305  }
6306  }
6307  }
6308  else
6309  {
6310  bool IncludeFER = false;
6311  if(IncNum == -1)
6312  {
6313  IncludeFER = true;
6314  }
6315  while(true) // finish commands & repeats break out of loop
6316  {
6317  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6318  if(!IncludeFER && (Ptr->Command == "Fer"))
6319  {
6320  break;
6321  }
6322  // Fer & included
6323  else if(IncludeFER && (Ptr->Command == "Fer"))
6324  {
6326  break;
6327  }
6328  // Repeat
6329  else if(Ptr->FormatType == Repeat)
6330  {
6331  break;
6332  }
6333  // Fjo
6334  else if(Ptr->Command == "Fjo")
6335  {
6337  break;
6338  }
6339  // Frh
6340  else if(Ptr->Command == "Frh")
6341  {
6343  {
6345  TerminatedMessageSent = true;
6346  }
6347  break;
6348  }
6349  // Frh-sh
6350  else if(Ptr->Command == "Frh-sh")
6351  {
6353  {
6355  TerminatedMessageSent = true;
6356  }
6357  break;
6358  }
6359  // Fns, F-nshs, Fns-sh
6360  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6361  {
6363  break;
6364  }
6365  // end of breakout actions
6366  // arrival
6367  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6368  {
6369  if(IsTrainTerminating(1))
6370  {
6372  TerminatedMessageSent = true;
6373  }
6374  else
6375  {
6377  }
6378  }
6379  // arrival & departure
6380  else if(Ptr->FormatType == TimeTimeLoc)
6381  {
6383  }
6384  // departure
6385  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6386  {
6387  Ptr++;
6388  continue; // skip TimeLoc departures, message given for arrivals
6389  }
6390  // pass
6391  else if(Ptr->FormatType == PassTime)
6392  {
6394  }
6395  // split
6396  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6397  {
6399  }
6400  // jbo
6401  else if(Ptr->Command == "jbo")
6402  {
6404  }
6405  // cdt
6406  else if(Ptr->Command == "cdt")
6407  {
6409  }
6410  // Errors
6411  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6412  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6413  {
6414  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6415  }
6416  Ptr++;
6417  }
6418  TimetableFinished = true;
6419  }
6420  Utilities->CallLogPop(1021);
6421 }
6422 
6423 // ---------------------------------------------------------------------------
6424 
6425 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6426 // ensure same repeatnumber
6427 {
6428  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6430 
6431  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6432  {
6433  Utilities->CallLogPop(1024);
6434  return(false);
6435  }
6436  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6437  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6438  {
6439  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6440  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6441  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6442  {
6443  Utilities->CallLogPop(1025);
6444  return(true);
6445  }
6446  }
6447  Utilities->CallLogPop(1026);
6448  return(false);
6449 }
6450 
6451 // ---------------------------------------------------------------------------
6452 
6453 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6454 // ensure same repeatnumber
6455 {
6456  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6457  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6458 
6459  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6460  {
6461  Utilities->CallLogPop(1027);
6462  return(false);
6463  }
6464  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6465  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6466  {
6467  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6468  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6469  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6470  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6471  {
6472  Utilities->CallLogPop(1028);
6473  return(true);
6474  }
6475  }
6476  Utilities->CallLogPop(1029);
6477  return(false);
6478 }
6479 
6480 // ---------------------------------------------------------------------------
6481 
6483 {
6484  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6485  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6486  if(PowerAtRail < 1)
6487  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6488  {
6490  {
6491  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6492  }
6494  Utilities->CallLogPop(2143);
6495  return;
6496  }
6497  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6498 
6500  UnplotTrain(4);
6503  StartSpeed = 0;
6508  HeadCode = NewHeadCode;
6509  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6510  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6511  StoppedAtLocation = true;
6512  PlotStartPosition(6);
6514  // pale green
6517  TerminatedMessageSent = false;
6518  Utilities->CallLogPop(1078);
6519 }
6520 
6521 // ---------------------------------------------------------------------------
6522 
6524 // need to check whether all repeats finished or not
6525 {
6526  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6527  if(RemainHereLogNotSent) // to prevent repeated logs
6528  {
6529  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6530  RemainHereLogNotSent = false;
6531  }
6533  // finished all repeats
6534  {
6536  {
6539  TerminatedMessageSent = true;
6540  // no need to clear message as no more actions
6541  }
6542  TimetableFinished = true;
6543  Utilities->CallLogPop(1080);
6544  return;
6545  }
6546  if(PowerAtRail < 1)
6547  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6548  {
6550  {
6551  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6552  }
6554  Utilities->CallLogPop(2144);
6555  return;
6556  }
6557  int TempRepeatNumber = RepeatNumber + 1;
6558  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6559  // until after LogAction or the wrong time will be used
6560  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6561 
6563  RepeatNumber++;
6564  UnplotTrain(5);
6567  StartSpeed = 0;
6572  HeadCode = NewHeadCode;
6573  StoppedAtLocation = true;
6574  PlotStartPosition(7);
6576  // pale green
6579  TerminatedMessageSent = false;
6580  Utilities->CallLogPop(1079);
6581 }
6582 
6583 // ---------------------------------------------------------------------------
6584 
6586 {
6587  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6588  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6589  if(PowerAtRail < 1)
6590  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6591  {
6593  {
6594  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6595  }
6597  Utilities->CallLogPop(2145);
6598  return;
6599  }
6601  // finished all repeats
6602  {
6603  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6605  RepeatNumber = 0;
6606  UnplotTrain(6);
6609  StartSpeed = 0;
6611  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6612  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6614  HeadCode = NewHeadCode;
6615  StoppedAtLocation = true;
6616  PlotStartPosition(9);
6620  TerminatedMessageSent = false;
6621  Utilities->CallLogPop(1081);
6622  return;
6623  }
6624  int TempRepeatNumber = RepeatNumber + 1;
6625  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6626  // until after LogAction or the wrong time will be used
6627  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6628 
6630  RepeatNumber++;
6631  UnplotTrain(7);
6634  StartSpeed = 0;
6639  HeadCode = NewHeadCode;
6640  StoppedAtLocation = true;
6641  PlotStartPosition(8);
6643  // pale green
6646  TerminatedMessageSent = false;
6647  Utilities->CallLogPop(1082);
6648 }
6649 
6650 // ---------------------------------------------------------------------------
6651 
6653 {
6654  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6655  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6656  // must be preceded by a TimeLoc departure
6657  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6658  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6659  {
6661  {
6662  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6663  {
6664  Utilities->CallLogPop(1083);
6665  return(false);
6666  }
6667  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6668  {
6669  Utilities->CallLogPop(1084);
6670  return(true);
6671  }
6672  }
6673  }
6674  Utilities->CallLogPop(1085);
6675  return(false);
6676 }
6677 
6678 // ---------------------------------------------------------------------------
6679 
6680 bool TTrain::AbleToMove(int Caller)
6681 {
6682  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6683  bool Able = true;
6684 
6685  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6686  {
6687  // StoppedForTrainInFront removed as tested below
6688  Able = false;
6689  Utilities->CallLogPop(2146); // added v2.4.0
6690  return(Able); // added v2.4.0
6691  }
6692  if(LeadElement > -1)
6693  {
6694  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6695  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6696  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6697  {
6698  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6699  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6700  {
6701  Able = true;
6702  StoppedForTrainInFront = false;
6703  }
6704  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6705  {
6706  Able = true;
6707  StoppedForTrainInFront = false;
6708  }
6709  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6710  {
6711  Able = true;
6712  StoppedForTrainInFront = false;
6713  }
6714  }
6715  else
6716  {
6718  {
6719  Able = false;
6720  }
6721  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6722  }
6723  }
6724  else // leaving at a continuation so keep going
6725  {
6726  Able = true;
6727  StoppedForTrainInFront = false;
6728  }
6729  Utilities->CallLogPop(1454);
6730  return(Able);
6731 }
6732 
6733 // ---------------------------------------------------------------------------
6734 
6736 {
6737  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6738  // won't be set; if there is a train then set StoppedForTrainInFront
6739  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6740  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6741  if(LeadElement == -1) // exiting at continuation
6742  {
6743  Utilities->CallLogPop(2045);
6744  return(false);
6745  }
6746  // end of addition
6747  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6748  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6749 
6750  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6751  {
6752  StoppedForTrainInFront = true;
6753  Utilities->CallLogPop(1455);
6754  return(false);
6755  }
6756  else
6757  {
6758  Utilities->CallLogPop(1456);
6760  // StoppedWithoutPower added v2.4.0
6761  }
6762 }
6763 
6764 // ---------------------------------------------------------------------------
6765 
6767 {
6768  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6769  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6770  TColor TempColour = BackgroundColour;
6771 
6772  UnplotTrain(8);
6775  StartSpeed = 0;
6776  PlotStartPosition(2);
6777  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6778 
6779  //now erase a stub route if there is one, added at v2.5.1
6780  //first element of route is now immediately behind the train (i.e. next to MidElement)
6781  if(MidEntryPos >= 0)
6782  {
6783  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6784  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6785  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6786  int RouteNumber = -1;
6787  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6788  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6789  {
6790  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6791  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6792  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6793  {
6794  bool FirstPass = true; //added at v2.8.0
6795  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6796  {
6797  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6798  int TVPos2 = PDE.GetTrackVectorPosition();
6799  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6800  {
6801  break;
6802  }
6803  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6805  {
6806  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6807  }
6808  else
6809  {
6810  break;
6811  }
6812  FirstPass = false;
6813  }
6814  AllRoutes->RebuildRailwayFlag = true;
6815  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6816  }
6817  }
6818  }
6819  Utilities->CallLogPop(1102);
6820 }
6821 
6822 // ---------------------------------------------------------------------------
6823 
6825 {
6826  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6827  ",FloatingLabelNextString" + "," + HeadCode);
6828  AnsiString RetStr = "", LocationName = "";
6829 
6830  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6831  {
6832  throw Exception("Error - start entry in FloatingLabelNextString");
6833  }
6834  if(Ptr->FormatType == TimeTimeLoc)
6835  {
6836  if(TrainMode == Timetable)
6837  {
6838  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6839  // not arrived yet in tt mode
6840  {
6841  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6842  }
6843  else
6844  {
6845  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6846  }
6847  }
6848  else // TrainMode == Signaller
6849  {
6850  if(!DepartureTimeSet) // not arrived yet
6851  {
6852  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6853  }
6854  else
6855  {
6856  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6857  }
6858  }
6859  }
6860  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6861  {
6862  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6863  }
6864  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6865  {
6866  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6867  }
6868  else if(Ptr->FormatType == PassTime) // new
6869  {
6870  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6871  }
6872  else if(Ptr->Command == "Fns")
6873  {
6874  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6875  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6876  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6877  }
6878  else if(Ptr->Command == "F-nshs")
6879  {
6880  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6882  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6883  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6884  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6885  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6886  }
6887  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6888  {
6889  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6890  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6891  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6892  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6893  }
6894  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6895  {
6896  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6897  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6898  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6899  }
6900  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6901  {
6902  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6903  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6904  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6905  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6906  }
6907  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6908  {
6909  RetStr ="None, train terminated at " + Ptr->LocationName;
6910  }
6911  else if(Ptr->Command == "Frh")
6912  {
6913  RetStr = "None, train terminated at " + Ptr->LocationName;
6914  }
6915  else if(Ptr->Command == "Fer")
6916  {
6917  AnsiString AllowedExits = "";
6918  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
6919  }
6920  else if(Ptr->Command == "Fjo")
6921  {
6922  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6924  }
6925  else if(Ptr->Command == "jbo")
6926  {
6927  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6928  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6929  }
6930  else if(Ptr->Command == "fsp")
6931  {
6932  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6933  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6934  }
6935  else if(Ptr->Command == "rsp")
6936  {
6937  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6938  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6939  }
6940  else if(Ptr->Command == "cdt")
6941  {
6942  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6943  }
6944  Utilities->CallLogPop(1124);
6945  return(RetStr);
6946 }
6947 
6948 // ---------------------------------------------------------------------------
6949 
6950 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6951 {
6952  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6953  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6954  AnsiString DepTime = "", EventTime = "";
6955  bool CDTFlag = false; //reports if train changes direction before departs
6956  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6957  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6958  {
6959  if(AVI->Command == "cdt")
6960  {
6961  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6962  continue;
6963  }
6964  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6965  {
6966  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6967  RetStr += "\nNew service splits at " + EventTime;
6968  Utilities->CallLogPop(2234);
6969  return(RetStr);
6970  }
6971  if(AVI->Command == "jbo")
6972  {
6973  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6974  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6975  Utilities->CallLogPop(2235);
6976  return(RetStr);
6977  }
6978  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
6979  {
6980  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
6981  if(CDTFlag)
6982  {
6983  RetStr += "\nNew service changes direction then departs at " + DepTime;
6984  }
6985  else
6986  {
6987  RetStr += "\nNew service departs at " + DepTime;
6988  }
6989  Utilities->CallLogPop(2236);
6990  return(RetStr);
6991  }
6992  }
6993  Utilities->CallLogPop(2208);
6994  return(RetStr); //if reach here then RetStr doesn't change
6995 }
6996 
6997 // ---------------------------------------------------------------------------
6998 
7000 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7001 {
7002  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7003  ",FloatingTimetableString" + "," + HeadCode);
7004  AnsiString RetStr = "", PartStr = "";
7005  int Count = 0;
7006 
7007  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7008  // can start in signaller control so exclude this
7009  {
7010  throw Exception("Error - start entry in FloatingTimetableString");
7011  }
7012  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
7013  bool FirstPass = true;
7014  Ptr--; // because incremented at start of loop
7015 
7016  // different first TimeTimeLoc display if in signaller control
7017  do
7018  {
7019  Ptr++;
7020  if((Ptr->FormatType == Repeat) || TimetableFinished)
7021  {
7022  break;
7023  }
7024  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7025  {
7026  AnsiString TrainLoc = "";
7027  if(TrainMode == Timetable)
7028  {
7029  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7030  {
7031  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7032  }
7033  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7034  {
7035  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7036  }
7037  else
7038  {
7039  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7040  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7041  Count++; // because there are 2 entries
7042  }
7043  }
7044  else // TrainMode == Signaller
7045  {
7046  if(DepartureTimeSet)
7047  {
7048  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7049  }
7050  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7051  {
7052  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7053  }
7054  else
7055  {
7056  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7057  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7058  Count++; // because there are 2 entries
7059  }
7060  }
7061  }
7062  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7063  {
7064  AnsiString TrainLoc = "";
7065  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7066  {
7067  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7068  }
7069  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7070  {
7071  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7072  }
7073  else
7074  {
7075  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7076  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7077  Count++; // because there are 2 entries
7078  }
7079  }
7080  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7081  {
7082  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7083  }
7084  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7085  {
7086  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7087  }
7088  else if(Ptr->FormatType == PassTime) // new
7089  {
7090  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7091  }
7092  else if(Ptr->Command == "Fns")
7093  {
7094  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7095  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7096  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7097  }
7098  else if(Ptr->Command == "F-nshs")
7099  {
7100  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7101  Ptr->LocationName;
7102  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7103  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7104  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7105  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7106  }
7107  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7108  {
7109  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7110  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7111  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7112  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7113  }
7114  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7115  {
7116  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7117  +" at " + Ptr->LocationName;
7118  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7119  }
7120  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7121  {
7122  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7123  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7124  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7125  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7126  }
7127  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7128  {
7129  PartStr = "Terminate at " + Ptr->LocationName;
7130  }
7131  else if(Ptr->Command == "Frh")
7132  {
7133  PartStr = "Terminate at " + Ptr->LocationName;
7134  }
7135  else if(Ptr->Command == "Fer")
7136  {
7137  AnsiString AllowedExits = "";
7138  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7139  }
7140  else if(Ptr->Command == "Fjo")
7141  {
7142  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7143  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7144  }
7145  else if(Ptr->Command == "jbo")
7146  {
7147  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7148  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7149  }
7150  else if(Ptr->Command == "fsp")
7151  {
7152  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7153  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7154  }
7155  else if(Ptr->Command == "rsp")
7156  {
7157  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7158  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7159  }
7160  else if(Ptr->Command == "cdt")
7161  {
7162  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7163  }
7164  if(RetStr != "")
7165  {
7166  RetStr = RetStr + '\n' + PartStr;
7167  }
7168  else
7169  {
7170  RetStr = PartStr;
7171  }
7172  FirstPass = false;
7173  Count++;
7174  }
7175  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7176  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7177  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7178  // forward as anyone should wish to see without looking at the full timetable
7179  if(TimetableFinished)
7180  {
7181  if(TrainMode == Timetable)
7182  {
7183  RetStr = "Timetable finished";
7184  }
7185  else
7186  {
7187  RetStr = "No timetable";
7188  }
7189  }
7190  Utilities->CallLogPop(1125);
7191  return(RetStr);
7192 }
7193 
7194 // ---------------------------------------------------------------------------
7195 
7196 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7197 {
7198  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7199  Utilities->SaveFileString(OutFile, HeadCode);
7202  Utilities->SaveFileInt(OutFile, StartSpeed);
7205  Utilities->SaveFileInt(OutFile, RepeatNumber);
7208  Utilities->SaveFileInt(OutFile, Mass);
7211  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7218  Utilities->SaveFileDouble(OutFile, BrakeRate);
7222  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7223  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7224  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7225  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7226  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7227  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7231  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7236  Utilities->SaveFileBool(OutFile, Derailed);
7238  Utilities->SaveFileBool(OutFile, Crashed);
7245  Utilities->SaveFileBool(OutFile, NotInService);
7246  Utilities->SaveFileBool(OutFile, Plotted);
7247  Utilities->SaveFileBool(OutFile, TrainGone);
7248  Utilities->SaveFileBool(OutFile, SPADFlag);
7250  Utilities->SaveFileInt(OutFile, HOffset[0]);
7251  Utilities->SaveFileInt(OutFile, HOffset[1]);
7252  Utilities->SaveFileInt(OutFile, HOffset[2]);
7253  Utilities->SaveFileInt(OutFile, HOffset[3]);
7254  Utilities->SaveFileInt(OutFile, VOffset[0]);
7255  Utilities->SaveFileInt(OutFile, VOffset[1]);
7256  Utilities->SaveFileInt(OutFile, VOffset[2]);
7257  Utilities->SaveFileInt(OutFile, VOffset[3]);
7258  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7259  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7260  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7261  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7262  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7263  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7264  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7265  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7267  Utilities->SaveFileInt(OutFile, (short)Straddle);
7268  Utilities->SaveFileInt(OutFile, NextTrainID);
7269  Utilities->SaveFileInt(OutFile, TrainID);
7270  Utilities->SaveFileInt(OutFile, LeadElement);
7271  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7272  Utilities->SaveFileInt(OutFile, LeadExitPos);
7273  Utilities->SaveFileInt(OutFile, MidElement);
7274  Utilities->SaveFileInt(OutFile, MidEntryPos);
7275  Utilities->SaveFileInt(OutFile, MidExitPos);
7276  Utilities->SaveFileInt(OutFile, LagElement);
7277  Utilities->SaveFileInt(OutFile, LagEntryPos);
7278  Utilities->SaveFileInt(OutFile, LagExitPos);
7279  int ColourNumber;
7280 
7282  {
7283  ColourNumber = 0;
7284  }
7286  {
7287  ColourNumber = 1;
7288  }
7290  {
7291  ColourNumber = 2;
7292  }
7294  {
7295  ColourNumber = 3;
7296  }
7298  {
7299  ColourNumber = 4;
7300  }
7302  {
7303  ColourNumber = 5;
7304  }
7306  {
7307  ColourNumber = 6;
7308  }
7310  {
7311  ColourNumber = 7;
7312  }
7314  {
7315  ColourNumber = 8;
7316  }
7318  {
7319  ColourNumber = 9;
7320  }
7322  {
7323  ColourNumber = 10;
7324  }
7326  {
7327  ColourNumber = 11;
7328  }
7330  {
7331  ColourNumber = 12;
7332  }
7333  else if(BackgroundColour == clTRSBackground)
7334  {
7335  ColourNumber = 13;
7336  }
7338  {
7339  ColourNumber = 14; // added at v2.4.0
7340  }
7341  Utilities->SaveFileInt(OutFile, ColourNumber);
7342 
7343  // additional data
7344  bool ForwardHeadCode;
7345 
7346  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7347  {
7348  ForwardHeadCode = true;
7349  }
7350  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7351  else
7352  {
7353  ForwardHeadCode = false;
7354  }
7355  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7356 
7357  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7358 
7359  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7360  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7361 
7362  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7363  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7364  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7365  // so use the last asterisk position for this - 0 for false & 1 for true
7366  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7367  AnsiString Marker;
7368 
7370  {
7371  Marker = "*****1";
7372  }
7373  else
7374  {
7375  Marker = "*****0";
7376  }
7377  if(RestoreTimetableLocation == "")
7378  {
7379  Utilities->SaveFileString(OutFile, Marker);
7380  }
7381  else
7382  {
7383  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7384  Utilities->SaveFileString(OutFile, CombinedString);
7385  // RestoreTimetableLocation + marker
7386  }
7387  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7388  Utilities->CallLogPop(1457);
7389 }
7390 
7391 // ---------------------------------------------------------------------------
7392 
7393 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7394 {
7395  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7396  HeadCode = Utilities->LoadFileString(InFile);
7399  StartSpeed = Utilities->LoadFileInt(InFile);
7401  if(SignallerMaxSpeed < 10)
7402  {
7403  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7404  }
7406  RepeatNumber = Utilities->LoadFileInt(InFile);
7409  Mass = Utilities->LoadFileInt(InFile);
7412  {
7414  }
7415  // above added at v2.1.0 for legacy session files where value may not have been limited
7417  EntrySpeed = Utilities->LoadFileDouble(InFile);
7421  if(TimetableMaxRunningSpeed < 10)
7422  {
7423  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7424  }
7426  if(MaxRunningSpeed < 10)
7427  {
7428  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7429  }
7432  BrakeRate = Utilities->LoadFileDouble(InFile);
7436  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7437  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7438  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7439  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7440  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7441  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7450  Derailed = Utilities->LoadFileBool(InFile);
7452  Crashed = Utilities->LoadFileBool(InFile);
7459  NotInService = Utilities->LoadFileBool(InFile);
7460  Plotted = Utilities->LoadFileBool(InFile);
7461  TrainGone = Utilities->LoadFileBool(InFile);
7462  SPADFlag = Utilities->LoadFileBool(InFile);
7464  HOffset[0] = Utilities->LoadFileInt(InFile);
7465  HOffset[1] = Utilities->LoadFileInt(InFile);
7466  HOffset[2] = Utilities->LoadFileInt(InFile);
7467  HOffset[3] = Utilities->LoadFileInt(InFile);
7468  VOffset[0] = Utilities->LoadFileInt(InFile);
7469  VOffset[1] = Utilities->LoadFileInt(InFile);
7470  VOffset[2] = Utilities->LoadFileInt(InFile);
7471  VOffset[3] = Utilities->LoadFileInt(InFile);
7472  PlotElement[0] = Utilities->LoadFileInt(InFile);
7473  PlotElement[1] = Utilities->LoadFileInt(InFile);
7474  PlotElement[2] = Utilities->LoadFileInt(InFile);
7475  PlotElement[3] = Utilities->LoadFileInt(InFile);
7476  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7477  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7478  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7479  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7481  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7482  NextTrainID = Utilities->LoadFileInt(InFile);
7483  // will be same for all but best to save all anyway
7484  TrainID = Utilities->LoadFileInt(InFile);
7485  LeadElement = Utilities->LoadFileInt(InFile);
7486  LeadEntryPos = Utilities->LoadFileInt(InFile);
7487  LeadExitPos = Utilities->LoadFileInt(InFile);
7488  MidElement = Utilities->LoadFileInt(InFile);
7489  MidEntryPos = Utilities->LoadFileInt(InFile);
7490  MidExitPos = Utilities->LoadFileInt(InFile);
7491  LagElement = Utilities->LoadFileInt(InFile);
7492  LagEntryPos = Utilities->LoadFileInt(InFile);
7493  LagExitPos = Utilities->LoadFileInt(InFile);
7494  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7495 
7496  if(ColourNumber == 0)
7497  {
7499  }
7500  else if(ColourNumber == 1)
7501  {
7503  }
7504  else if(ColourNumber == 2)
7505  {
7507  }
7508  else if(ColourNumber == 3)
7509  {
7511  }
7512  else if(ColourNumber == 4)
7513  {
7515  }
7516  else if(ColourNumber == 5)
7517  {
7519  }
7520  else if(ColourNumber == 6)
7521  {
7523  }
7524  else if(ColourNumber == 7)
7525  {
7527  }
7528  else if(ColourNumber == 8)
7529  {
7531  }
7532  else if(ColourNumber == 9)
7533  {
7535  }
7536  else if(ColourNumber == 10)
7537  {
7539  }
7540  else if(ColourNumber == 11)
7541  {
7543  }
7544  else if(ColourNumber == 12)
7545  {
7547  }
7548  else if(ColourNumber == 13)
7549  {
7551  }
7552  else if(ColourNumber == 14)
7553  {
7554  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7555 
7556  }
7557  // additional data
7559  // sets the BackgroundColour to the loaded value
7560  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7561 
7562  if(ForwardHeadCode)
7563  {
7564  for(int x = 0; x < 4; x++)
7565  {
7567  }
7568  }
7569  else
7570  {
7571  for(int x = 0; x < 4; x++)
7572  {
7573  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7574  }
7575  }
7576  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7577  if(TrainMode == Timetable)
7578  {
7579  if(Crashed)
7580  {
7582  }
7583  else
7584  {
7586  }
7587  }
7588  else
7589  {
7591  }
7593  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7594  if(Straddle == LeadMid)
7595  {
7596  if(LeadElement > -1)
7597  {
7599  }
7600  if(LeadElement > -1)
7601  {
7603  }
7604  if(MidElement > -1)
7605  {
7607  }
7608  if(MidElement > -1)
7609  {
7611  }
7612  }
7613  else if(Straddle == LeadMidLag)
7614  {
7615  if(LeadElement > -1)
7616  {
7618  }
7619  if(MidElement > -1)
7620  {
7622  }
7623  if(MidElement > -1)
7624  {
7626  }
7627  if(LagElement > -1)
7628  {
7630  }
7631  }
7632  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7633 
7634  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7635  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7636 
7637  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7638 
7639  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7640  if(LeadElement > -1)
7641  // need to include this in case train exiting & no lead element
7642  {
7644  {
7645  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7646  }
7647  }
7648  AValue = sqrt(2 * PowerAtRail / Mass);
7649 
7650  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7651 
7652  // possible RestoreTimetableLocation + Marker, where Marker is
7653  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7654  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7655  // added at beta v0.2e
7656  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7657  // name not allowed to include the '*' character
7658  {
7659  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7660  bool GiveMessagesFalse = false;
7661  bool CheckLocationsExistInRailwayTrue = true;
7662  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7663  {
7664  // otherwise take no action
7665  RestoreTimetableLocation = Location;
7666  }
7667  }
7668  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7669 
7670  StoppedWithoutPower = false;
7671  if(Marker[6] == '1')
7672  {
7673  StoppedWithoutPower = true;
7674  }
7675  Utilities->CallLogPop(1458);
7676 }
7677 
7678 // ---------------------------------------------------------------------------
7679 
7680 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7681 {
7683  {
7684  return(false); // HeadCode
7685 
7686  }
7687  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7688  {
7689  return(false); // RearStartElement
7690 
7691  }
7692  if(!Utilities->CheckFileInt(InFile, 0, 3))
7693  {
7694  return(false); // RearStartExitPos
7695 
7696  }
7697  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7698  {
7699  return(false); // StartSpeed
7700 
7701  }
7702  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7703  {
7704  return(false); // SignallerMaxSpeed
7705 
7706  }
7707  if(!Utilities->CheckFileBool(InFile))
7708  {
7709  return(false); // HoldAtLocationInTTMode
7710 
7711  }
7712  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7713  {
7714  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7715 
7716  }
7717  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7718  {
7719  return(false); // IncrementalMinutes (max 96 x 60)
7720 
7721  }
7722  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7723  {
7724  return(false); // IncrementalDigits
7725 
7726  }
7727  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7728  {
7729  return(false); // Mass
7730 
7731  }
7732  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7733  {
7734  return(false);
7735  }
7736  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7737  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7738  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7739  {
7740  return(false); // FrontElementLength
7741 
7742  }
7743  if(!Utilities->CheckFileDouble(InFile))
7744  {
7745  return(false); // EntrySpeed
7746 
7747  }
7748  if(!Utilities->CheckFileDouble(InFile))
7749  {
7750  return(false); // ExitSpeedHalf
7751 
7752  }
7753  if(!Utilities->CheckFileDouble(InFile))
7754  {
7755  return(false); // ExitSpeedFull
7756 
7757  }
7758  if(!Utilities->CheckFileDouble(InFile))
7759  {
7760  return(false); // TimetableMaxRunningSpeed
7761 
7762  }
7763  if(!Utilities->CheckFileDouble(InFile))
7764  {
7765  return(false); // MaxRunningSpeed
7766 
7767  }
7768  if(!Utilities->CheckFileDouble(InFile))
7769  {
7770  return(false); // MaxExitSpeed
7771 
7772  }
7773  if(!Utilities->CheckFileDouble(InFile))
7774  {
7775  return(false); // MaxBrakeRate
7776 
7777  }
7778  if(!Utilities->CheckFileDouble(InFile))
7779  {
7780  return(false); // BrakeRate
7781 
7782  }
7783  if(!Utilities->CheckFileDouble(InFile))
7784  {
7785  return(false); // PowerAtRail
7786 
7787  }
7788  if(!Utilities->CheckFileBool(InFile))
7789  {
7790  return(false); // FirstHalfMove
7791 
7792  }
7793  if(!Utilities->CheckFileBool(InFile))
7794  {
7795  return(false); // OneLengthAccelDecel
7796 
7797  }
7798  if(!Utilities->CheckFileDouble(InFile))
7799  {
7800  return(false); // double(EntryTime)
7801 
7802  }
7803  if(!Utilities->CheckFileDouble(InFile))
7804  {
7805  return(false); // double(ExitTimeHalf)
7806 
7807  }
7808  if(!Utilities->CheckFileDouble(InFile))
7809  {
7810  return(false); // double(ExitTimeFull)
7811 
7812  }
7813  if(!Utilities->CheckFileDouble(InFile))
7814  {
7815  return(false); // double(ReleaseTime)
7816 
7817  }
7818  if(!Utilities->CheckFileDouble(InFile))
7819  {
7820  return(false); // double(TRSTime)
7821 
7822  }
7823  if(!Utilities->CheckFileDouble(InFile))
7824  {
7825  return(false); // double(LastActionTime)
7826 
7827  }
7828  if(!Utilities->CheckFileBool(InFile))
7829  {
7830  return(false); // CallingOnFlag
7831 
7832  }
7833  if(!Utilities->CheckFileBool(InFile))
7834  {
7835  return(false); // BeingCalledOn
7836 
7837  }
7838  if(!Utilities->CheckFileBool(InFile))
7839  {
7840  return(false); // DepartureTimeSet
7841 
7842  }
7843  if(!Utilities->CheckFileInt(InFile, 0, 2))
7844  {
7845  return(false); // (short)TrainMode
7846 
7847  }
7848  if(!Utilities->CheckFileBool(InFile))
7849  {
7850  return(false); // TimetableFinished
7851 
7852  }
7853  if(!Utilities->CheckFileBool(InFile))
7854  {
7855  return(false); // LastActionDelayFlag
7856 
7857  }
7858  if(!Utilities->CheckFileBool(InFile))
7859  {
7860  return(false); // SignallerRemoved
7861 
7862  }
7863  if(!Utilities->CheckFileBool(InFile))
7864  {
7865  return(false); // TerminatedMessageSent
7866 
7867  }
7868  if(!Utilities->CheckFileBool(InFile))
7869  {
7870  return(false); // Derailed
7871 
7872  }
7873  if(!Utilities->CheckFileBool(InFile))
7874  {
7875  return(false); // DerailPending
7876 
7877  }
7878  if(!Utilities->CheckFileBool(InFile))
7879  {
7880  return(false); // Crashed
7881 
7882  }
7883  if(!Utilities->CheckFileBool(InFile))
7884  {
7885  return(false); // StoppedAtBuffers
7886 
7887  }
7888  if(!Utilities->CheckFileBool(InFile))
7889  {
7890  return(false); // StoppedAtSignal
7891 
7892  }
7893  if(!Utilities->CheckFileBool(InFile))
7894  {
7895  return(false); // StoppedAtLocation
7896 
7897  }
7898  if(!Utilities->CheckFileBool(InFile))
7899  {
7900  return(false); // SignallerStopped
7901 
7902  }
7903  if(!Utilities->CheckFileBool(InFile))
7904  {
7905  return(false); // StoppedAfterSPAD
7906 
7907  }
7908  if(!Utilities->CheckFileBool(InFile))
7909  {
7910  return(false); // StoppedForTrainInFront
7911 
7912  }
7913  if(!Utilities->CheckFileBool(InFile))
7914  {
7915  return(false); // NotInService
7916 
7917  }
7918  if(!Utilities->CheckFileBool(InFile))
7919  {
7920  return(false); // Plotted
7921 
7922  }
7923  if(!Utilities->CheckFileBool(InFile))
7924  {
7925  return(false); // TrainGone
7926 
7927  }
7928  if(!Utilities->CheckFileBool(InFile))
7929  {
7930  return(false); // SPADFlag
7931 
7932  }
7933  if(!Utilities->CheckFileBool(InFile))
7934  {
7935  return(false); // TimeTimeLocArrived
7936 
7937  }
7938  if(!Utilities->CheckFileInt(InFile, 0, 15))
7939  {
7940  return(false); // HOffset[0]
7941 
7942  }
7943  if(!Utilities->CheckFileInt(InFile, 0, 15))
7944  {
7945  return(false); // HOffset[1]
7946 
7947  }
7948  if(!Utilities->CheckFileInt(InFile, 0, 15))
7949  {
7950  return(false); // HOffset[2]
7951 
7952  }
7953  if(!Utilities->CheckFileInt(InFile, 0, 15))
7954  {
7955  return(false); // HOffset[3]
7956 
7957  }
7958  if(!Utilities->CheckFileInt(InFile, 0, 15))
7959  {
7960  return(false); // VOffset[0]
7961 
7962  }
7963  if(!Utilities->CheckFileInt(InFile, 0, 15))
7964  {
7965  return(false); // VOffset[1]
7966 
7967  }
7968  if(!Utilities->CheckFileInt(InFile, 0, 15))
7969  {
7970  return(false); // VOffset[2]
7971 
7972  }
7973  if(!Utilities->CheckFileInt(InFile, 0, 15))
7974  {
7975  return(false); // VOffset[3]
7976 
7977  }
7978  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7979  {
7980  return(false); // PlotElement[0]
7981 
7982  }
7983  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7984  {
7985  return(false); // PlotElement[1]
7986 
7987  }
7988  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7989  {
7990  return(false); // PlotElement[2]
7991 
7992  }
7993  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7994  {
7995  return(false); // PlotElement[3]
7996 
7997  }
7998  if(!Utilities->CheckFileInt(InFile, 0, 3))
7999  {
8000  return(false); // PlotEntryPos[0]
8001 
8002  }
8003  if(!Utilities->CheckFileInt(InFile, 0, 3))
8004  {
8005  return(false); // PlotEntryPos[1]
8006 
8007  }
8008  if(!Utilities->CheckFileInt(InFile, 0, 3))
8009  {
8010  return(false); // PlotEntryPos[2]
8011 
8012  }
8013  if(!Utilities->CheckFileInt(InFile, 0, 3))
8014  {
8015  return(false); // PlotEntryPos[3]
8016 
8017  }
8018  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8019  {
8020  return(false); // TrainCrashedInto
8021 
8022  }
8023  if(!Utilities->CheckFileInt(InFile, 0, 2))
8024  {
8025  return(false); // (short)Straddle
8026 
8027  }
8028  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8029  {
8030  return(false); // NextTrainID
8031 
8032  }
8033  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8034  {
8035  return(false); // TrainID
8036 
8037  }
8038  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8039  {
8040  return(false); // LeadElement
8041 
8042  }
8043  if(!Utilities->CheckFileInt(InFile, 0, 3))
8044  {
8045  return(false); // LeadEntryPos
8046 
8047  }
8048  if(!Utilities->CheckFileInt(InFile, 0, 3))
8049  {
8050  return(false); // LeadExitPos
8051 
8052  }
8053  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8054  {
8055  return(false); // MidElement
8056 
8057  }
8058  if(!Utilities->CheckFileInt(InFile, 0, 3))
8059  {
8060  return(false); // MidEntryPos
8061 
8062  }
8063  if(!Utilities->CheckFileInt(InFile, 0, 3))
8064  {
8065  return(false); // MidExitPos
8066 
8067  }
8068  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8069  {
8070  return(false); // LagElement
8071 
8072  }
8073  if(!Utilities->CheckFileInt(InFile, 0, 3))
8074  {
8075  return(false); // LagEntryPos
8076 
8077  }
8078  if(!Utilities->CheckFileInt(InFile, 0, 3))
8079  {
8080  return(false); // LagExitPos
8081 
8082  }
8083  if(!Utilities->CheckFileInt(InFile, 0, 14))
8084  {
8085  return(false);
8086  }
8087  // Background colour number //14 is failed colour at v2.4.0
8088  if(!Utilities->CheckFileBool(InFile))
8089  {
8090  return(false); // ForwardHeadCode
8091 
8092  }
8093  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8094  {
8095  return(false); // TrainDataEntryValue
8096 
8097  }
8098  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8099  {
8100  return(false); // ActionVectorEntryValue
8101 
8102  }
8104  {
8105  return(false); // End of train marker + possible RestoreTimetableLocation
8106 
8107  }
8108  // and StoppedWithoutPower flag
8109  return(true);
8110 }
8111 
8112 // ---------------------------------------------------------------------------
8113 
8114 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8115 {
8116  // order below reflects significance so earlier shows first, as may have more than one flag set
8117  // only plot flashing trains when Flash is true
8118 
8119 /*
8120  clCrashedBackground (TColor)0x0000FF red
8121  clDerailedBackground (TColor)0x0000FF red
8122  clSPADBackground (TColor)0x00FFFF yellow
8123  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8124  clCallOnBackground (TColor)0xFF33FF light magenta
8125  clSignalStopBackground (TColor)0x00FF66 green
8126  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8127  clStationStopBackground (TColor)0xCCFFCC pale green
8128  clTRSBackground (TColor)0xFFCCFF light pink
8129  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8130  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8131  clSignallerStopped (TColor)0x99CCFF caramel
8132  clNormalBackground (TColor)0xCCCCCC grey
8133 */
8134 
8135  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8136  bool HideFlashingTrain = true;
8137  // hide it when Flash false so it blinks on and off
8138  // if don't hide it it stays displayed all the time
8139  Graphics::TBitmap *SmallTrainBitmap;
8140 
8141  // NB ensure retain same order as zoomed in order so colours correspond
8143  {
8144  TrainController->CrashWarning = true;
8145  SmallTrainBitmap = RailGraphics->smRed;
8146  }
8148  {
8150  SmallTrainBitmap = RailGraphics->smRed;
8151  }
8153  {
8154  TrainController->SPADWarning = true;
8155  SmallTrainBitmap = RailGraphics->smYellow;
8156  }
8158  {
8160  SmallTrainBitmap = RailGraphics->smOrange;
8161  }
8163  {
8165  SmallTrainBitmap = RailGraphics->smMagenta;
8166  }
8168  {
8170  SmallTrainBitmap = RailGraphics->smBrightGreen;
8171  }
8173  {
8175  SmallTrainBitmap = RailGraphics->smCyan;
8176  }
8178  {
8179  SmallTrainBitmap = RailGraphics->smPaleGreen;
8180  HideFlashingTrain = false;
8181  }
8183  {
8184  SmallTrainBitmap = RailGraphics->smCyan;
8185  HideFlashingTrain = false;
8186  }
8188  {
8189  SmallTrainBitmap = RailGraphics->smLightBlue;
8190  HideFlashingTrain = false;
8191  }
8193  {
8194  SmallTrainBitmap = RailGraphics->smCaramel;
8195  HideFlashingTrain = false;
8196  }
8197  else
8198  {
8199  SmallTrainBitmap = RailGraphics->smBlack; // moving
8200  HideFlashingTrain = false;
8201  }
8202  // now plot the new train
8203  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8204  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8205  {
8206  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8207  }
8208  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8209  {
8210  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8211  }
8212  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8213  {
8214  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8215  }
8219  Utilities->CallLogPop(1459);
8220 }
8221 
8222 // ---------------------------------------------------------------------------
8223 
8225 {
8226  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8227  if(!Display->ZoomOutFlag)
8228  {
8229  Utilities->CallLogPop(1304);
8230  return;
8231  }
8232  for(int y = 0; y < 3; y++)
8233  {
8234  if(OldZoomOutElement[y] > -1)
8235  {
8236  bool FoundFlag = false;
8237  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8238  TTrackElement IATElement1, IATElement2;
8239  // default elements to begin with
8240  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8241  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8242  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8243  if(FoundFlag)
8244  {
8245  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8246  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8247  if(IMPair.first != IMPair.second)
8248  {
8249  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8250  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8251  }
8252  }
8253  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8254  }
8255  }
8256  Utilities->CallLogPop(1305);
8257 }
8258 
8259 // ---------------------------------------------------------------------------
8260 
8261 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8262 {
8263  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8264  LocationName = "";
8265  if(!StoppedAtLocation)
8266  {
8267  Utilities->CallLogPop(1398);
8268  return(false);
8269  }
8270  if(LeadElement > -1)
8271  {
8273  }
8274  if((LocationName == "") && (MidElement > -1))
8275  {
8276  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8277  }
8278  if((LocationName == "") && (LagElement > -1))
8279  {
8280  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8281  }
8282  if(LocationName == "")
8283  {
8284  throw Exception("Error - Location name not set in TrainAtLocation");
8285  }
8286  Utilities->CallLogPop(1399);
8287  return(true);
8288 }
8289 
8290 // ---------------------------------------------------------------------------
8291 
8292 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8293 {
8294  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8295  for(int x = 0; x < 4; x++)
8296  {
8297  PlotTrainGraphic(7, x, Disp);
8298  }
8299  Utilities->CallLogPop(647);
8300 }
8301 
8302 // ---------------------------------------------------------------------------
8303 
8304 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8305 {
8306  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8307  for(int x = 0; x < 4; x++)
8308  {
8309  if(PlotElement[x] > -1)
8310  {
8311  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8312  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8313  }
8314  }
8315  Utilities->CallLogPop(1708);
8316 }
8317 
8318 // ---------------------------------------------------------------------------
8319 
8320 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8321 {
8322  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8323  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8324  AnsiString(LinkNumber) + "," + HeadCode);
8325 
8326 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8327  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8328  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8329 */
8330 
8331  // note that MidElement always fully occupied
8332  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8333  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8334  {
8335  Utilities->CallLogPop(2005);
8336  return(true);
8337  }
8338  if(Straddle == LeadMid)
8339  {
8340  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8341  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8342  {
8343  Utilities->CallLogPop(2006);
8344  return(true);
8345  }
8346  }
8347  else if(Straddle == LeadMidLag)
8348  {
8349  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8350  // only interested in LeadEntryPos as train not occupying ExitPos yet
8351  {
8352  Utilities->CallLogPop(2007);
8353  return(true);
8354  }
8355  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8356  // only interested in LagExitPos as train has left EntryPos
8357  {
8358  Utilities->CallLogPop(2008);
8359  return(true);
8360  }
8361  }
8362  Utilities->CallLogPop(2009);
8363  return(false);
8364 }
8365 
8366 // ---------------------------------------------------------------------------
8367 
8368 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
8373 {
8374  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8375  int DistanceToRedSignal = 0;
8376  float TimeToAct = 0;
8377  float MinsEarly = 0; //added at v2.6.1
8378  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8379  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8380 
8381  if(TrainFailed)
8382  {
8383  Utilities->CallLogPop(2147);
8384  return(0); // time to act now
8385  }
8386  if(SignallerStopped)
8387  {
8388  Utilities->CallLogPop(2080);
8389  return(-1);
8390  }
8391  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8392  {
8393  Utilities->CallLogPop(2266);
8394  return(-1);
8395  }
8396  if(!Stopped() || StoppedAtLocation)
8397  {
8398  // calc distance to next red signal but check for continuation exit
8399  if(LeadElement == -1)
8400  // if -1 it's on an end element so no action needed
8401  {
8402  Utilities->CallLogPop(2075);
8403  return(-1);
8404  }
8405  else
8406  {
8407  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8408  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8409  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8410 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8411  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8412  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8413  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8414  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8415  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8416  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8417  before the train has stopped the current station is still recognised as a future stop.
8418  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8419  becomes the signal, and the time to act indication becomes 'NOW'.
8420 */
8421  {
8422  FirstPosToBeMeasured = LeadElement;
8423  FirstEntryPos = LeadEntryPos;
8424  }
8425  float CurrentStopTime; // set to 0 at start of function
8426  float LaterStopTime; // set to 0 at start of function
8427  float RecoverableTime; // set to 0 at start of function
8428  int AvTrackSpeed; // set to zero at start of function
8429  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8430  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8431  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
8432  if(DistanceToRedSignal == -1) // -1 for no action needed
8433  {
8434  Utilities->CallLogPop(2076);
8435  return(-1);
8436  }
8437 /* Have MinsDelayed; pos or neg,
8438  CurrentStopTime; pos or zero
8439  LaterStopTime; pos or zero
8440  RecoverableTime; pos or zero
8441 
8442  & from these calculate TotalStopTime. noting that:
8443  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8444  RecoverableTime always < LaterStopTime or both zero
8445  can't subtract more than RecoverableTime (MinsDelayed > 0)
8446  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8447  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8448  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8449  if running early & stopped at location CurrentStopTime will automatically include the excess
8450 */
8451  float TimeToSubtract, TotalStopTime;
8452  if(MinsDelayed > RecoverableTime)
8453  {
8454  TimeToSubtract = RecoverableTime;
8455  }
8456  else
8457  {
8458  TimeToSubtract = MinsDelayed; // may be negative;
8459 
8460  }
8461  if((AvTrackSpeed > 0) && (DistanceToStationStop <= DistanceToRedSignal) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8462  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8463  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8464  //next station stop
8465  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8466  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8467  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8468  //first find departure time from the next stop
8469  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8470  {
8471  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8472  {
8475  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8476  }
8477  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8478  {
8480  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8481  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8482  {
8483  // must be a departure
8484  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8485  }
8486  }
8487  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8488  {
8489  MinsEarly = 0;
8490  }
8491  if(MinsEarly < 0)
8492  {
8493  MinsEarly = 0;
8494  }
8495  }
8496  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8497  {
8498  if(CurrentStopTime > 0)
8499  {
8500  TotalStopTime = CurrentStopTime + LaterStopTime;
8501  }
8502  // stopped at loc, will depart on time
8503  else
8504  {
8505  TotalStopTime = LaterStopTime - MinsDelayed;
8506  }
8507  // not stopped, will depart on time at first later stop so add the delay
8508  }
8509  else if((MinsEarly > 0) && !Stopped()) //running early
8510  {
8511  TotalStopTime = LaterStopTime + MinsEarly;
8512  }
8513  else // on time or running late
8514  {
8515  if(LaterStopTime == 0)
8516  {
8517  TotalStopTime = CurrentStopTime;
8518  }
8519  // no later stops, if stopped now will depart as soon as possible,
8520  // if not stopped no stop times to add
8521  else
8522  {
8523  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8524  }
8525  }
8526  if(AvTrackSpeed < 30)
8527  {
8528  AvTrackSpeed = 30;
8529  }
8530  int Speed = AvTrackSpeed;
8531  if(AvTrackSpeed > int(MaxRunningSpeed))
8532  {
8533  Speed = int(MaxRunningSpeed);
8534  }
8535  if(TrainMode == Signaller)
8536  {
8537  Speed = SignallerMaxSpeed;
8538  TotalStopTime = 0;
8539  }
8540  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
8541  // accel & decel taken into account in
8542  // CalcDistanceToRedSignalandStopTime
8543  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8544  Utilities->CallLogPop(2079);
8545  return(TimeToAct);
8546  }
8547  }
8548  else // stopped not at location
8549  {
8551  {
8552  TimeToAct = 0.0;
8553  }
8554  // but if stopped at a signal & autosigs route after it then ok
8555  if(StoppedAtSignal)
8556  {
8557  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8558  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8559  int NextExitPos;
8560  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8561  {
8562  if((NextEntryPos == 0) || (NextEntryPos == 2))
8563  // leading entry point
8564  {
8565  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8566  {
8567  NextExitPos = 1;
8568  }
8569  else
8570  {
8571  NextExitPos = 3;
8572  }
8573  }
8574  else
8575  {
8576  NextExitPos = 0; // trailing entry point
8577  }
8578  }
8579  else
8580  {
8581  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8582  }
8583  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8584  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8585  int RouteNumber; // holder for referenced value, not used
8586  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8587  {
8588  TimeToAct = -1;
8589  }
8590  }
8591  Utilities->CallLogPop(2074);
8592  return(TimeToAct);
8593  }
8594 }
8595 
8596 // ---------------------------------------------------------------------------
8597 
8599 {
8600  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8601  if(LeadElement > -1)
8602  {
8604  {
8605  Utilities->CallLogPop(2148);
8606  return(true);
8607  }
8608  }
8609  if(MidElement > -1)
8610  {
8612  {
8613  Utilities->CallLogPop(2149);
8614  return(true);
8615  }
8616  }
8617  if(LagElement > -1)
8618  {
8620  {
8621  Utilities->CallLogPop(2150);
8622  return(true);
8623  }
8624  }
8625  Utilities->CallLogPop(2151);
8626  return(false);
8627 }
8628 
8629 // ---------------------------------------------------------------------------
8630 // TTrainController
8631 // ---------------------------------------------------------------------------
8632 
8634 {
8635  OnTimeArrivals = 0;
8636  LateArrivals = 0;
8637  EarlyArrivals = 0;
8638  OnTimePasses = 0;
8639  LatePasses = 0;
8640  EarlyPasses = 0;
8641  OnTimeDeps = 0;
8642  LateDeps = 0;
8643  MissedStops = 0;
8644  OtherMissedEvents = 0;
8645  UnexpectedExits = 0;
8646  NumFailures = 0;
8647  IncorrectExits = 0;
8648  SPADEvents = 0;
8649  SPADRisks = 0;
8650  CrashedTrains = 0;
8651  Derailments = 0;
8652  TotArrDepPass = 0;
8653  TotLateArrMins = 0;
8654  TotEarlyArrMins = 0;
8655  TotLatePassMins = 0;
8656  TotEarlyPassMins = 0;
8657  TotLateDepMins = 0;
8658  ExcessLCDownMins = 0;
8659  TTClockTime = 0; // added for v0.6
8661  // added at v1.3.0 to ensure false at start
8662  OpTimeToActUpdateCounter = 0; // new v2.2.0
8663  OpActionPanelVisible = false; // new v2.2.0
8664  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
8665  SSHigh = false;
8666  MRSHigh = false;
8667  MRSLow = false;
8668  MassHigh = false;
8669  BFHigh = false;
8670  BFLow = false;
8671  PwrHigh = false;
8672  SigSHigh = false;
8673  SigSLow = false;
8674  randomize();
8675  // to seed rand() & random() with a random number (see UpdateTrain)
8676 }
8677 
8678 // ---------------------------------------------------------------------------
8679 
8681 {
8682  for(unsigned int x = 0; x < TrainVector.size(); x++)
8683  {
8684  TrainVectorAt(32, x).DeleteTrain(4);
8685  }
8686  TrainVector.clear();
8687 }
8688 
8689 // ---------------------------------------------------------------------------
8690 
8691 void TTrainController::LogEvent(AnsiString Str)
8692 {
8693  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
8694 
8695  // restrict to last 1000 entries
8696  Utilities->EventLog.push_back(FullStr);
8697  if(Utilities->EventLog.size() > 1000)
8698  {
8699  Utilities->EventLog.pop_front();
8700  }
8701 }
8702 
8703 // ---------------------------------------------------------------------------
8704 
8706 {
8707  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
8708  bool ClockState = Utilities->Clock2Stopped;
8709 
8710  Utilities->Clock2Stopped = true;
8711  // new section dealing with Snt & Snt-sh additions
8712  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
8713  // clock tick after stops flashing
8715  {
8716  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8717  {
8718  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8719  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8720  TActionEventType EventType = NoEvent;
8721  if(AVEntry0.Command == "Snt")
8722  {
8723  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8724  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8725  int IncrementalMinutes = 0;
8726  int IncrementalDigits = 0;
8727  if(AVEntryLast.FormatType == Repeat)
8728  {
8729  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8730  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8731  }
8732  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8733  {
8734  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8735  }
8736  // see above note
8737 
8738  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8739  {
8740  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8741  if(TTOD.RunningEntry != NotStarted)
8742  {
8743  continue;
8744  }
8745  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8746  {
8747  break; // all the rest will also be greater
8748  }
8749  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8750  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8751  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8752  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8753  {
8754  TTOD.TrainID = TrainVector.back().TrainID;
8755  TTOD.RunningEntry = Running;
8756  }
8757  else if(EventType == FailTrainEntry)
8758  {
8759  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8760  }
8761  }
8762  }
8763  if(AVEntry0.Command == "Snt-sh")
8764  // just start this once, shuttle repeats take care of restarts
8765  {
8766  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8767  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8768  int IncrementalMinutes = 0;
8769  int IncrementalDigits = 0;
8770  if(AVEntryLast.FormatType == Repeat)
8771  {
8772  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8773  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8774  }
8775  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8776  {
8777  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8778  }
8779  // see above note
8780  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8781  if(TTOD.RunningEntry == NotStarted)
8782  {
8783  if(AVEntry0.EventTime <= TTClockTime)
8784  {
8785  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8786  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8787  TDEntry.SignallerSpeed, false, EventType))
8788  // false for SignallerControl
8789  {
8790  TTOD.TrainID = TrainVector.back().TrainID;
8791  TTOD.RunningEntry = Running;
8792  }
8793  else if(EventType == FailTrainEntry)
8794  {
8795  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8796  }
8797  }
8798  }
8799  }
8800  }
8801  }
8802  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8803  // iteration, next cycle will catch up with any other pending updates
8804  if(!TrainVector.empty())
8805  {
8806  TrainAdded = false;
8807  AllRoutes->CallonVector.clear();
8808  // this will be rebuilt during the calls to UpdateTrain
8809  for(unsigned int x = 0; x < TrainVector.size(); x++)
8810  {
8811  TrainVectorAt(33, x).UpdateTrain(0);
8812  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8813  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8814  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8815  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8816  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8817  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8818  {
8819  break;
8820  }
8821  }
8822  // set warning flags
8823  CrashWarning = false;
8824  DerailWarning = false;
8825  SPADWarning = false;
8826  CallOnWarning = false;
8827  SignalStopWarning = false;
8828  BufferAttentionWarning = false;
8829  TrainFailedWarning = false;
8830  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8831  {
8832  TTrain &Train = TrainVectorAt(34, x);
8833  if(Train.Crashed)
8834  // can't use background colours for crashed & derailed because same colour
8835  {
8836  CrashWarning = true;
8837  }
8838  else if(Train.Derailed)
8839  // can't use background colours for crashed & derailed because same colour
8840  {
8841  DerailWarning = true;
8842  }
8843  else if(Train.BackgroundColour == clSPADBackground)
8844  // use colour as that changes as soon as passes signal
8845  {
8846  SPADWarning = true;
8847  }
8848  else if(Train.BackgroundColour == clTrainFailedBackground)
8849  {
8850  TrainFailedWarning = true;
8851  }
8852  else if(Train.BackgroundColour == clCallOnBackground)
8853  // use colour as also stopped at signal
8854  {
8855  CallOnWarning = true;
8856  }
8857  else if(Train.BackgroundColour == clSignalStopBackground)
8858  // use colour to distinguish from call-on
8859  {
8860  SignalStopWarning = true;
8861  }
8862  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8863  // use colour to distinguish from ordinary buffer stop
8864  {
8865  BufferAttentionWarning = true;
8866  }
8867  if(Train.HasTrainGone())
8868  {
8869  AnsiString Loc = "";
8870  bool ElementFound = false;
8871  TTrackElement TE;
8872  if(Train.LagElement > -1)
8873  {
8874  TE = Track->TrackElementAt(531, Train.LagElement);
8875  ElementFound = true;
8876  }
8877  else if(Train.MidElement > -1)
8878  {
8879  TE = Track->TrackElementAt(779, Train.MidElement);
8880  ElementFound = true;
8881  }
8882  else if(Train.LeadElement > -1)
8883  {
8884  TE = Track->TrackElementAt(780, Train.LeadElement);
8885  ElementFound = true;
8886  }
8887  if(ElementFound)
8888  {
8889  if(TE.ActiveTrackElementName != "")
8890  {
8891  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8892  }
8893  else
8894  {
8895  Loc = "track element " + TE.ElementID;
8896  }
8897  }
8898  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8899  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8900  // need above first because may also have ActionVectorEntryPtr == "Fer"
8901  {
8902  Train.UnplotTrain(9);
8903  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8905  {
8908  }
8909  // end of addition
8910  AllRoutes->RebuildRailwayFlag = true;
8911  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8912  // correctly after a crash
8913  }
8914  else if(AVEntryPtr->Command == "Fer")
8915  {
8916  bool CorrectExit = false;
8917  if(!AVEntryPtr->ExitList.empty())
8918  {
8919  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8920  {
8921  if(*ELIT == Train.LagElement)
8922  {
8923  CorrectExit = true;
8924  }
8925  }
8926  }
8927  if(CorrectExit)
8928  {
8929  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8930  }
8931  else
8932  {
8933  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8934  }
8935  }
8936  else
8937  {
8938  if(!AVEntryPtr->SignallerControl)
8939  {
8940  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8941  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8942  // -2 is marker for send messages for all remaining actions except Fer if present
8943  }
8944  else
8945  {
8946  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8947  }
8948  }
8949  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8950  Train.DeleteTrain(1);
8951  TrainVector.erase(TrainVector.begin() + x);
8952  ReplotTrains(1, Display);
8953  // to reset ElementIDs for remaining trains when have removed a train
8954  }
8955  }
8956  }
8957  else
8958  {
8959  // reset all flags in case last train removed with flag set
8960  CrashWarning = false;
8961  DerailWarning = false;
8962  SPADWarning = false;
8963  CallOnWarning = false;
8964  SignalStopWarning = false;
8965  BufferAttentionWarning = false;
8966  TrainFailedWarning = false;
8967  }
8968  // update OpTimeToActMultimap
8970  {
8972  // clears entries then adds values for running trains then for continuation entries
8973  }
8974  Utilities->Clock2Stopped = ClockState;
8975  Utilities->CallLogPop(723);
8976 }
8977 
8978 // ---------------------------------------------------------------------------
8980 {
8981  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
8982  if(!TrainVector.empty())
8983  {
8984  for(int x = TrainVector.size() - 1; x >= 0; x--)
8985  {
8986  TrainVectorAt(50, x).DeleteTrain(2);
8987  }
8988  TrainVector.clear();
8989  }
8990  if(!TrainDataVector.empty())
8991  {
8992  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8993  {
8994  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
8995  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8996  {
8997  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
8998  TOD.RunningEntry = NotStarted;
8999  TOD.TrainID = -1;
9000  TOD.EventReported = NoEvent;
9001  }
9002  }
9003  }
9004  Display->GetOutputLog1()->Caption = "";
9005  Display->GetOutputLog2()->Caption = "";
9006  Display->GetOutputLog3()->Caption = "";
9007  Display->GetOutputLog4()->Caption = "";
9008  Display->GetOutputLog5()->Caption = "";
9009  Display->GetOutputLog6()->Caption = "";
9010  Display->GetOutputLog7()->Caption = "";
9011  Display->GetOutputLog8()->Caption = "";
9012  Display->GetOutputLog9()->Caption = "";
9013  Display->GetOutputLog10()->Caption = "";
9014  Utilities->CallLogPop(1352);
9015 }
9016 
9017 // ---------------------------------------------------------------------------
9018 
9020 {
9021  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9022  if(!TrainVector.empty())
9023  {
9024  for(unsigned int x = 0; x < TrainVector.size(); x++)
9025  {
9026  TrainVectorAt(51, x).PlotTrain(4, Disp);
9027  }
9028  }
9029  Utilities->CallLogPop(724);
9030 }
9031 
9032 // ---------------------------------------------------------------------------
9033 
9034 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9035 {
9036  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9037  if(!TrainVector.empty())
9038  {
9039  for(unsigned int x = 0; x < TrainVector.size(); x++)
9040  {
9041  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9042  }
9043  }
9044  Utilities->CallLogPop(1707);
9045 }
9046 
9047 // ---------------------------------------------------------------------------
9048 
9050 {
9051  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9052  if(!TrainVector.empty())
9053  {
9054  for(unsigned int x = 0; x < TrainVector.size(); x++)
9055  {
9056  TrainVectorAt(52, x).UnplotTrain(10);
9057  }
9058  }
9060  Utilities->CallLogPop(725);
9061 }
9062 
9063 // ---------------------------------------------------------------------------
9064 
9065 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9066  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9067  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9068 {
9069  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9070  "," + AnsiString(Mass) + "," + ModeStr);
9071  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9072  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
9073 
9074  int RearExitPos = -1;
9075 
9076  for(int x = 0; x < 4; x++)
9077  {
9078  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9079  {
9080  RearExitPos = x;
9081  }
9082  }
9083  if(RearExitPos == -1)
9084  {
9085  throw Exception("Error, RearExit == -1 in AddTrain");
9086  }
9087  bool ReportFlag = true;
9088 
9089  // used to stop repeated messages from CheckStartAllowable when split failed
9090  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9091  {
9092  ReportFlag = false;
9093  }
9094  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9095  {
9096  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9097  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9098  Utilities->CallLogPop(938);
9099  return(false);
9100  }
9101  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9102  TTrainMode TrainMode = NoMode;
9103 
9104  if(ModeStr == "Timetable")
9105  {
9106  TrainMode = Timetable;
9107  }
9108  // all else gives 'None', 'Signaller' set within program
9109 
9110  if(MaxRunningSpeed < 10)
9111  {
9112  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9113  }
9114  if(SignallerSpeed < 10)
9115  {
9116  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9117  }
9118  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9119  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9120 
9121  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9122  // initialise here rather than in TTrain constructor as create trains
9123  // with Null TrainDataEntryPtr when loading session trains
9124  if(SignallerControl)
9125  {
9126  NewTrain->TimetableFinished = true;
9127  NewTrain->SignallerStoppingFlag = false;
9128  NewTrain->TrainMode = Signaller;
9129  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9130  {
9131  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9132  }
9134  }
9135  // deal with starting conditions:-
9136  // unlocated Snt: just report entry & advance pointer
9137  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9138  // Sns doesn't need a new train
9139  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9140  // covers all above located starts
9141  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9142  // wouldn't have accepted the timetable
9143  {
9144  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9145  // StoppedAtBuffers is set in UpdateTrain()
9146  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9147  // buffer end must be ahead of train or would have failed start position check
9148  {
9149  NewTrain->StoppedAtLocation = true;
9150  NewTrain->PlotStartPosition(0);
9152  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9153  NewTrain->ActionVectorEntryPtr->Warning);
9154  if(!SignallerControl) // don't advance if SignalControlEntry
9155  {
9156  NewTrain->ActionVectorEntryPtr++;
9157  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9158  }
9159  NewTrain->LastActionTime = TTClockTime;
9160  }
9161  // else a through station stop
9162  else
9163  {
9164  NewTrain->StoppedAtLocation = true;
9165  NewTrain->PlotStartPosition(10);
9167  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9168  NewTrain->ActionVectorEntryPtr->Warning);
9169  if(!SignallerControl) // don't advance if SignalControlEntry
9170  {
9171  NewTrain->ActionVectorEntryPtr++;
9172  }
9173  NewTrain->LastActionTime = TTClockTime;
9174  }
9175  }
9176  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9177  {
9178  NewTrain->PlotStartPosition(11);
9179  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9180  AnsiString Loc = "";
9181  if(TE.ActiveTrackElementName != "")
9182  {
9183  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9184  }
9185  else
9186  {
9187  Loc = "track element " + TE.ElementID;
9188  }
9189  if(TE.TrackType == Continuation)
9190  {
9191  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9192  }
9193  else
9194  {
9195  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9196  }
9197  if(!SignallerControl) // don't advance if SignalControlEntry
9198  {
9199  NewTrain->ActionVectorEntryPtr++;
9200  }
9201  NewTrain->LastActionTime = TTClockTime;
9202  // no need to set LastActionTime for an unlocated entry
9203  }
9204  // cancel a wrong-direction route if either element of train starts on one
9205  if(NewTrain->LeadElement > -1)
9206  {
9207  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9208  }
9209  if(NewTrain->MidElement > -1)
9210  {
9211  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9212  }
9213  // set signals for a right-direction autosigs route for either element of train on one
9214  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9215  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9216  int RouteNumber = -1;
9217  bool SignalsSet = false;
9218 
9219  if(NewTrain->LeadElement > -1)
9220  {
9221  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9222  {
9223  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9224  int RouteStartPosition;
9225  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9226  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9227  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9228  if(FirstPair.first == RouteNumber)
9229  {
9230  RouteStartPosition = FirstPair.second;
9231  }
9232  else if(SecondPair.first == RouteNumber)
9233  {
9234  RouteStartPosition = SecondPair.second;
9235  }
9236  else
9237  {
9238  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9239  }
9240  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9241  SignalsSet = true;
9242  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9243  }
9244  else if(RouteNumber > -1) // non-autosigsroute
9245  {
9246  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9247  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9248  int FirstELinkPos = TempPDE.GetELinkPos();
9249  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9250  {
9251  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9252  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9253  }
9254  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9255  {
9256  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9257  // remove the last element under LeadElement
9258  }
9259  AllRoutes->RebuildRailwayFlag = true;
9260  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9261  // now deal with a rear linked autosigs route
9262  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9263  {
9264  int LinkedRouteNumber = -1;
9265  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9266  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9267  {
9268  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9269  // this is ok as here we are setting signals from the start of the route
9270  }
9271  }
9272  SignalsSet = true;
9273  }
9274  }
9275  if(NewTrain->MidElement > -1)
9276  // if entering at a continuation MidElement == -1
9277  {
9278  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9279  if(!SignalsSet)
9280  {
9281  RouteNumber = -1;
9282  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9283  {
9284  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9285  int RouteStartPosition;
9286  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9287  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9288  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9289  if(FirstPair.first == RouteNumber)
9290  {
9291  RouteStartPosition = FirstPair.second;
9292  }
9293  else if(SecondPair.first == RouteNumber)
9294  {
9295  RouteStartPosition = SecondPair.second;
9296  }
9297  else
9298  {
9299  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9300  }
9301  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9302  SignalsSet = true;
9303  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9304  }
9305  else if(RouteNumber > -1) // non-autosigsroute
9306  {
9307  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9308  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9309  int FirstELinkPos = TempPDE.GetELinkPos();
9310  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9311  {
9312  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9313  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9314  }
9315  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9316  {
9317  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9318  // remove the last element under LeadElement
9319  }
9320  AllRoutes->RebuildRailwayFlag = true;
9321  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9322  // now deal with a rear linked autosigs route
9323  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9324  {
9325  int LinkedRouteNumber = -1;
9326  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9327  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9328  {
9329  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9330  // this is ok as now we are setting signals from the start of the route
9331  }
9332  }
9333  }
9334  }
9335  }
9336  TrainVector.push_back(*NewTrain);
9337  Utilities->CallLogPop(731);
9338  return(true);
9339 }
9340 
9341 // ---------------------------------------------------------------------------
9342 
9343 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9344 {
9345  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9346  AnsiString(TrackVectorNumber));
9347  int VecPos = -1;
9348 
9349  for(unsigned int x = 0; x < TrainVector.size(); x++)
9350  {
9351  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9352  {
9353  VecPos = x;
9354  }
9355  }
9356  if(VecPos == -1)
9357  {
9358  throw Exception("Error, VecPos not set in EntryPos");
9359  }
9360  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9361  {
9362  Utilities->CallLogPop(734);
9363  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9364  }
9365  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9366  {
9367  Utilities->CallLogPop(735);
9368  return(TrainVectorAt(5, VecPos).MidEntryPos);
9369  }
9370  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9371  {
9372  Utilities->CallLogPop(736);
9373  return(TrainVectorAt(7, VecPos).LagEntryPos);
9374  }
9375  Utilities->CallLogPop(737);
9376  return(-1);
9377 }
9378 
9379 // ---------------------------------------------------------------------------
9380 
9382 {
9383  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9384  for(unsigned int x = 0; x < TrainVector.size(); x++)
9385  {
9386  if(TrainVectorAt(53, x).TrainID == TrainID)
9387  {
9388  Utilities->CallLogPop(738);
9389  return(TrainVectorAt(54, x));
9390  }
9391  }
9392  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9393 }
9394 
9395 // ---------------------------------------------------------------------------
9396 
9397 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9398 // return true if find the train (added at v2.4.0 as can select a removed train
9399 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9400 {
9401  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9402  for(unsigned int x = 0; x < TrainVector.size(); x++)
9403  {
9404  if(TrainVectorAt(69, x).TrainID == TrainID)
9405  {
9406  Utilities->CallLogPop(2152);
9407  return(true);
9408  }
9409  }
9410  Utilities->CallLogPop(2153);
9411  return(false);
9412 }
9413 
9414 // ---------------------------------------------------------------------------
9415 
9416 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9417 {
9418  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9419  Utilities->Format96HHMMSS(Time));
9420  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9421 
9422  Utilities->CallLogPop(2061);
9423  return(RepeatTime);
9424 }
9425 
9426 // ---------------------------------------------------------------------------
9427 
9428 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9429 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9430 {
9431  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9432  AnsiString RetStr = "", PartStr = "";
9433  int Count = 0;
9434  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9435 
9436  Ptr--; // because incremented at start of loop
9437  do
9438  {
9439  Ptr++;
9440  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9441  {
9442  continue; // move past the starting entry
9443  }
9444  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9445  {
9446  break;
9447  }
9448  if(Ptr->SignallerControl)
9449  {
9450  RetStr = "Train under signaller control";
9451  break;
9452  }
9453  if(Ptr->FormatType == TimeTimeLoc)
9454  {
9455  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9456  {
9457  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9458  }
9459  else
9460  {
9461  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9462  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9463  Count++; // because there are 2 entries
9464  }
9465  }
9466  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9467  {
9468  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9469  }
9470  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9471  {
9472  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9473  }
9474  else if(Ptr->FormatType == PassTime) // new
9475  {
9476  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9477  }
9478  else if(Ptr->Command == "Fns")
9479  {
9480  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9481  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9482  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9483  }
9484  else if(Ptr->Command == "F-nshs")
9485  {
9486  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9487  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9488  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9489  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9490  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9491  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9492  }
9493 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9494  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9495  {
9496  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9497  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9498  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9499  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9500  }
9501  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9502  {
9503  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9504  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9505  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9506  }
9507  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9508  {
9509  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9510  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9511  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9512  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9513  }
9514  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9515  {
9516  PartStr = "Terminate at " + Ptr->LocationName;
9517  }
9518  else if(Ptr->Command == "Frh")
9519  {
9520  PartStr = "Terminate at " + Ptr->LocationName;
9521  }
9522  else if(Ptr->Command == "Fer")
9523  {
9524  AnsiString AllowedExits;
9525  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9526  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9527  }
9528  else if(Ptr->Command == "Fjo")
9529  {
9530  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9531  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9532  }
9533  else if(Ptr->Command == "jbo")
9534  {
9535  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9536  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9537  }
9538  else if(Ptr->Command == "fsp")
9539  {
9540  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9541  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9542  }
9543  else if(Ptr->Command == "rsp")
9544  {
9545  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9546  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9547  }
9548  else if(Ptr->Command == "cdt")
9549  {
9550  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9551  }
9552  if(RetStr != "")
9553  {
9554  RetStr = RetStr + '\n' + PartStr;
9555  }
9556  else
9557  {
9558  RetStr = PartStr;
9559  }
9560  Count++;
9561  }
9562  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
9563  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
9564  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
9565  // forward as anyone should wish to see without looking at the full timetable
9566  Utilities->CallLogPop(2072);
9567  return(RetStr);
9568 }
9569 
9570 // ---------------------------------------------------------------------------
9571 
9572 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
9573 {
9574  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
9575  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
9576  AnsiString DepTime = "", EventTime = "";
9577  bool CDTFlag = false; //reports if train changes direction before departs
9578  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
9579  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
9580  {
9581  if(AVI->Command == "cdt")
9582  {
9583  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
9584  continue;
9585  }
9586  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
9587  {
9588  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
9589  RetStr += "\nNew service splits at " + EventTime;
9590  Utilities->CallLogPop(2237);
9591  return(RetStr);
9592  }
9593  if(AVI->Command == "jbo")
9594  {
9595  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
9596  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
9597  Utilities->CallLogPop(2238);
9598  return(RetStr);
9599  }
9600  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
9601  {
9602  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
9603  if(CDTFlag)
9604  {
9605  RetStr += "\nNew service changes direction then departs at " + DepTime;
9606  }
9607  else
9608  {
9609  RetStr += "\nNew service departs at " + DepTime;
9610  }
9611  Utilities->CallLogPop(2239);
9612  return(RetStr);
9613  }
9614  }
9615  Utilities->CallLogPop(2223);
9616  return(RetStr);
9617 }
9618 
9619 // ---------------------------------------------------------------------------
9620 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
9621 /*
9622  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
9623  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
9624  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
9625 
9626  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
9627  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
9628  user wishes
9629 
9630  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9631  descriptive text or anything user wishes
9632  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9633  be ignored) is taken as the timetable start time.
9634  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9635  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9636  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9637  within the timetable if required.
9638  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9639  services)
9640  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9641  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9642 
9643  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9644  text timetable file easier
9645 
9646  form:-
9647  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9648  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9649  then multiple entries, separated by commas, of the form:-
9650 
9651  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9652  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
9653  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
9654 
9655  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
9656  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
9657  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
9658 
9659  HH:MM;Command (cdt) }TimeCmd }
9660  HH:MM;Location (arr & dep) }TimeLoc }
9661  HH:MM;HH:MM;Location }TimeTimeLoc }
9662  HH:MM;pas;Location }PassTime }
9663  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
9664  HH:MM;Fer;set of allowable IDs }ExitRailway }
9665  Command (Frh only) }FinRemHere }
9666 
9667  R;mm;dd;nn. Repeat Repeat entry
9668 
9669  Formats:
9670 
9671  Command only: Frh
9672  Time;Command: cdt
9673  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
9674  Time;Command;2 Element IDs: Snt
9675  Time;Comand;n Element IDs: Fer
9676  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
9677  Time;Command;2 Element IDs;Headcode Snt-sh
9678  Time;Command;Location pas
9679  Time;Location Arr Dep
9680  Time;Time;Location Arr & dep together
9681 
9682  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
9683 
9684  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
9685  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
9686  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
9687 
9688  4 2x Linked entries, all shuttles:
9689 
9690  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
9691  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
9692  -> Remain Here (at finish location after all repeats)
9693  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
9694  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
9695 
9696  Allowable successors:-
9697 
9698  Successor state Type
9699 
9700  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
9701  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
9702  Sfs AtLoc )
9703  Sns AtLoc ) Start
9704  Sns-fsh AtLoc )
9705  Snt-sh AtLoc )
9706  Sns-sh AtLoc )
9707 
9708  pas Moving )
9709  jbo AtLoc )
9710  fsp AtLoc )
9711  rsp AtLoc ) Intermediate
9712  cdt AtLoc )
9713  TimeLoc arr Moving (bef) )
9714  TimeLoc dep AtLoc (bef) )
9715  TimeTimeLoc Moving )
9716 
9717  Fns Repeat/Nothing)
9718  Fjo Repeat/Nothing)
9719  Frh Repeat/Nothing)
9720  Fer Repeat/Nothing) Finish
9721  Frh-sh Repeat )
9722  Fns-sh Repeat )
9723  F-nshs Nothing )
9724 
9725  Descriptions:
9726  Snt New train
9727  Sfs New service from split
9728  Sns New service from another service
9729  Sns-fsh New non-repeating service from a shuttle service
9730  Snt-sh New shuttle train at a timetabled stop
9731  Sns-sh New shuttle service from a feeder service
9732 
9733  pas Pass
9734  jbo Be joined by another train
9735  fsp Front split
9736  rsp Rear split
9737  cdt Change direction of train
9738  TimeLoc arr Arrival
9739  TimeLoc dep Departure
9740  TimeTimeLoc Arrival and departure
9741 
9742  Fns Finish & form a new service
9743  Fjo Finish & join another train
9744  Frh Finish & remain here
9745  Fer Finish & exit railway
9746  Frh-sh Finish & repeat shuttle, finally remain here
9747  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9748  F-nshs Finish & form a shuttle feeder service
9749 */
9750 
9751 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9752 {
9753  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9754  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9755  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9756  // new for v0.2b
9757  // compile ActiveTrackElementNameMap
9758  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9759 
9761  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9762  {
9763  // if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->TrackVector.at(x).TrackType != Continuation))
9764  if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->ContinuationNameMap.find(Track->TrackVector.at(x).ActiveTrackElementName))
9765  == Track->ContinuationNameMap.end())
9766  {
9767  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9768  ActiveTrackElementNameMapEntry.first = Track->TrackVector.at(x).ActiveTrackElementName;
9769  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9770  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9771  }
9772  }
9774  // end of new section
9775  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9776 
9777  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9778  if(TTBLFile.is_open())
9779  {
9780  char *TrainTimetableString = new char[10000];
9781  // enough for over 200 stations, should be adequate!
9782  bool EndOfFile = false;
9783  int Count = 0;
9784  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9785  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9786  // delimiter is '\0' as it's an AnsiString
9787  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9788  // file empty - stores a null in 1st position if doesn't load any characters
9789  {
9790  // may still have eof even if read a line (no CRLF at end), and
9791  // if so need to process it
9792  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9793  TTBLFile.close();
9794  delete[] TrainTimetableString;
9795  Utilities->CallLogPop(1611);
9796  return(false);
9797  }
9798  AnsiString OneLine(TrainTimetableString);
9799  bool FinalCallFalse = false;
9800  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9801  // get rid of lines before the start time
9802  {
9803  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9804  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9805  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9806  // stores a null in 1st position if doesn't load any characters
9807  {
9808  // may still have eof even if read a line (no CRLF at end), and
9809  // if so need to process it
9810  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9811  TTBLFile.close();
9812  delete[] TrainTimetableString;
9813  Utilities->CallLogPop(772);
9814  return(false);
9815  }
9816  OneLine = AnsiString(TrainTimetableString);
9817  }
9818  // here when have accepted the start time
9819  Count++; // increment past the start time
9820  while(!EndOfFile)
9821  {
9822  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9823  // get next line after start time
9824  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9825  // stores a null in 1st position if doesn't load any characters
9826  {
9827  // may still have eof even if read a line (no CRLF at end), and
9828  // if so need to process it
9829  EndOfFile = true;
9830  OneLine = "";
9831  }
9832  else
9833  {
9834  OneLine = AnsiString(TrainTimetableString);
9835  }
9836  if(OneLine.Length() > 9999)
9837  {
9838  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9839  TTBLFile.close();
9840  delete[] TrainTimetableString;
9841  Utilities->CallLogPop(789);
9842  return(false);
9843  }
9844  bool FinalCallFalse = false;
9845  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9846  // false for FinalCall - just checking at this stage
9847  {
9848  TTBLFile.close();
9849  delete[] TrainTimetableString;
9850  Utilities->CallLogPop(770);
9851  return(false);
9852  }
9853  if(EndOfFile && (Count < 2))
9854  // Timetable must contain at least two relevant lines, one for start time and at least one train
9855  {
9856  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9857  TTBLFile.close();
9858  delete[] TrainTimetableString;
9859  Utilities->CallLogPop(771);
9860  return(false);
9861  }
9862  Count++;
9863  }
9864  delete[] TrainTimetableString;
9865  TTBLFile.close();
9866  } // if(TTBLFile.is_open())
9867  else
9868  {
9869  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's not open in another application");
9870  Utilities->CallLogPop(2154);
9871  return(false);
9872  }
9873  Utilities->CallLogPop(753);
9874  return(true);
9875 }
9876 
9877 // ---------------------------------------------------------------------------
9878 
9879 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9880  bool CheckLocationsExistInRailway) // return true for success
9881 
9882 /* Format:
9883  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9884  descriptive text or anything user wishes
9885  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9886  be ignored) is taken as the timetable start time.
9887  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9888  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9889  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9890  within the timetable if required.
9891  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9892  services)
9893  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9894  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9895 
9896  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9897  text timetable file easier
9898 
9899  form:-
9900  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9901  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9902  then multiple entries, separated by commas, of the form:-
9903 
9904  Format FormatType
9905  [W]HH:MM;Command (cdt) }TimeCmd }
9906  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9907  [W]HH:MM;pas;Location }PassTime }
9908  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9909  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9910  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9911  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9912  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9913  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9914  [W]HH:MM;Fns-sh;Details }FSHNewService }
9915  [W]HH:MM;Location (arr & dep) }TimeLoc }
9916  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9917  Command (Frh only) }FinRemHere }
9918 
9919  R;mm;dd;nn. Repeat Repeat entry
9920 
9921  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9922  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9923  at location; or (c) departure time if train already at location (including train started at location either as a new
9924  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9925  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9926  minutes, incremental train headcode last 2 digits, and number of repeats.
9927 
9928  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9929  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9930  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9931  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9932  (it's for a shuttle train to return to depot at end of services)
9933 
9934  Command/Location & details are as follows:-
9935 
9936  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9937  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9938  2E44 in its Sfs entry. All these are checked.
9939  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9940 
9941  Start commands:-
9942  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9943  with loc as a start entry can't have a location as details)
9944  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9945  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9946  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9947  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9948  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9949  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9950  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9951  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9952  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9953  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9954  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9955 
9956  Intermediate commands:-
9957  Time - Location (TimeLoc), can be arrival or departure depending on context
9958  Time Time location (TimeTimeLoc), arrival and departure
9959  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9960  pas (PassTime), Time;pas;Location
9961  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9962  joining train's finish details must correspond or the file check will fail
9963  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9964  new train - that train's starting information must correspond)
9965  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9966  new train - that train's starting information must correspond)
9967  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9968 
9969  Finish commands:-
9970  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9971  creation)
9972  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9973  shuttle headcode (no train creation)
9974  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
9975  may have to wait for it), details = new headcode (delete train)
9976  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
9977  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
9978  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
9979  here
9980  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
9981  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
9982 
9983  Repeat:-
9984  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
9985  headcodes - it is up to user to avoid duplicates if he/she wishes to.
9986 
9987  Checks carried out with error messages in this function:-
9988  At least one comma in a service line (it's based on a .csv file)
9989  No entries following train information;
9990  At least one comma in remainder after train information (i.e at least a start and a finish entry);
9991  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
9992  First entry not a start entry;
9993  Train information incomplete before a start entry;
9994  Entry follows a finish entry but doesn't begin with 'R';
9995  SplitEntry returns false in a finish entry - message repeats the entry for information;
9996  Last action entry isn't a finish entry.
9997 
9998  Function returns false with no message if:-
9999  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10000  time is found at all then an error message is given in the calling function);
10001  SplitTrainInfo returns false (message given in called function);
10002  SplitRepeat returns false (message given in called function).
10003 */{
10004  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10005  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10006  TTrainDataEntry TempTrainDataEntry;
10007 
10008  EndOfFile = false;
10009  StripSpaces(0, OneLine);
10010  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10011  // semicolons within the line
10012  ServiceReference = "";
10013  if(OneLine != "")
10014  {
10015  if(OneLine[1] != '*')
10016  {
10017  int SCPos = OneLine.Pos(';');
10018  if(SCPos == 0)
10019  {
10020  ServiceReference = OneLine.SubString(1, 8);
10021  }
10022  else
10023  {
10024  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10025  }
10026  }
10027  }
10028  bool AllCommas = true;
10029 
10030  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10031  {
10032  if(OneLine[x] != ',')
10033  {
10034  AllCommas = false;
10035  }
10036  }
10037  if(AllCommas || (OneLine == ""))
10038  {
10039  if(Count > 0)
10040  {
10041  EndOfFile = true;
10042  // returns true for a blank line - treated as end of file
10043  Utilities->CallLogPop(1018);
10044  return(true);
10045  }
10046  else // count == 0 so not yet found a start time, no message to be given
10047  {
10048  Utilities->CallLogPop(754);
10049  return(false);
10050  }
10051  }
10052  AnsiString First = "", Second = "", Third = "", Fourth = "";
10053  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10054  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10055  TDateTime StartTime(0);
10056  TExitList ExitList;
10057  bool Warning = false;
10058 
10059  if(Count == 0) // no start time found yet
10060  {
10061 /* dropped at v0.6b
10062  AnyHeadCodeValid = false;
10063  if(OneLine.SubString(6,5) == ";0000")
10064  {
10065  AnyHeadCodeValid = true;
10066  }
10067 */
10068  if(!CheckTimeValidity(0, OneLine, StartTime))
10069  {
10070  // no message is given for an invalid time as it's assumed to be an irrelevant line
10071  // if no start time is found at all then an error message is given in the calling function
10072  // AnyHeadCodeValid = false;
10073  Utilities->CallLogPop(755);
10074  return(false);
10075  }
10076  if(FinalCall) // here if start time valid
10077  {
10078  TTClockTime = StartTime;
10079  TimetableStartTime = StartTime;
10080  }
10081  }
10082  else
10083  {
10084  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10085  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10086  double MaxBrakeRate = 0;
10087  double PowerAtRail = 0;
10088  int SignallerSpeed = 0;
10089  if(OneLine[1] == '*')
10090  {
10091  Utilities->CallLogPop(1581);
10092  return(true);
10093  // ignore any line beginning with '*' but return true as there is no error
10094  }
10095  int Pos = OneLine.Pos(',');
10096  if(Pos == 0)
10097  {
10098  int SubStringLength = 20;
10099  if(OneLine.Length() < 20)
10100  {
10101  SubStringLength = OneLine.Length();
10102  }
10103  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10104  Utilities->CallLogPop(766);
10105  return(false);
10106  }
10107  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10108  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10109  GiveMessages)) // error messages given in SplitTrainInfo
10110  {
10111  Utilities->CallLogPop(773);
10112  return(false);
10113  }
10114  if(FinalCall)
10115  {
10116  // store Train info - conversions done in SplitTrainInfo
10117  // only headcode mandatory for continuing services
10118  TempTrainDataEntry.HeadCode = HeadCode;
10119  TempTrainDataEntry.ServiceReference = HeadCode;
10120  TempTrainDataEntry.Description = Description;
10121  TempTrainDataEntry.StartSpeed = StartSpeed;
10122  TempTrainDataEntry.Mass = Mass;
10123  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10124  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10125  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10126  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10127  TTrainOperatingData TempTrainOperatingData;
10128  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10129  }
10130  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10131  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10132  // so strip them off
10133  while(NewRemainder[NewRemainder.Length()] == ',')
10134  {
10135  if(NewRemainder.Length() > 1)
10136  {
10137  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10138  }
10139  else
10140  {
10141  NewRemainder = "";
10142  break;
10143  }
10144  }
10145  // check if zero length & fail if so
10146  if(NewRemainder == "")
10147  {
10148  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10149  Utilities->CallLogPop(769);
10150  return(false);
10151  }
10152  // now have one more entry than there are commas
10153  int CommaCount = 0;
10154  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10155  {
10156  if(NewRemainder[x] == ',')
10157  {
10158  CommaCount++;
10159  }
10160  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10161  if(CommaCount == 0)
10162  {
10163  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10164  {
10165  int SubStringLength = 20;
10166  if(OneLine.Length() < 20)
10167  {
10168  SubStringLength = OneLine.Length();
10169  }
10170  TimetableMessage(GiveMessages,
10171  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10172  OneLine.SubString(1, SubStringLength) + "'....");
10173  Utilities->CallLogPop(783);
10174  return(false);
10175  }
10176  }
10177  AnsiString OneEntry = "";
10178  TTimetableFormatType FormatType;
10179  TTimetableSequenceType SequenceType;
10180  TTimetableLocationType LocationType;
10181  TTimetableShuttleLinkType ShuttleLinkType;
10182  bool FinishFlag = false;
10183  for(int x = 0; x < CommaCount + 1; x++)
10184  {
10185  if((CommaCount == 0) || (x < CommaCount))
10186  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10187  // in which case there's a comma & finish element or repeat still to come this entry could
10188  // be a finish but can't be a repeat
10189  {
10190  if(CommaCount == 0)
10191  {
10192  OneEntry = NewRemainder;
10193  NewRemainder = "";
10194  }
10195  else
10196  {
10197  Pos = NewRemainder.Pos(',');
10198  OneEntry = NewRemainder.SubString(1, Pos - 1);
10199  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10200  }
10201  First = "";
10202  Second = "";
10203  Third = "";
10204  Fourth = "";
10205  RearStartOrRepeatMins = 0;
10206  FrontStartOrRepeatDigits = 0;
10207  NumberOfRepeats = 0;
10208  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10209  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10210  {
10211  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10212  Utilities->CallLogPop(756);
10213  return(false);
10214  }
10215  // check if warning for Frh or Fjo & reject
10216  if(Warning && (Second == "Frh"))
10217  {
10218  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10219  Utilities->CallLogPop(1793);
10220  return(false);
10221  }
10222  if(Warning && (Second == "Fjo"))
10223  {
10224  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10225  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10226  Utilities->CallLogPop(1794);
10227  return(false);
10228  }
10229  if(x == 0) // should be start event
10230  {
10231  if(SequenceType != Start)
10232  {
10233  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10234  Utilities->CallLogPop(784);
10235  return(false);
10236  }
10237  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10238  {
10239  if(NewRemainder[1] != 'R')
10240  {
10241  TimetableMessage(GiveMessages,
10242  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10243  OneEntry + "'");
10244  Utilities->CallLogPop(787);
10245  return(false);
10246  }
10247  }
10248  if((Second == "Snt") || (Second == "Snt-sh"))
10249  // need full train information including non-default values for at least HeadCode, Description,
10250  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10251  {
10252  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10253  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10254  {
10255  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10256  OneEntry + "'");
10257  Utilities->CallLogPop(1783);
10258  return(false);
10259  }
10260  }
10261  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10262  // service continuation - need at least non-default value for HeadCode
10263  {
10264  if(HeadCode == "")
10265  {
10266  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10267  OneEntry + "'");
10268  Utilities->CallLogPop(788);
10269  return(false);
10270  }
10271  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10272  {
10273  TimetableMessage(GiveMessages,
10274  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10275  OneEntry + "'");
10276  Utilities->CallLogPop(843);
10277  return(false);
10278  }
10279  }
10280  }
10281  if(SequenceType == Finish)
10282  {
10283  FinishFlag = true;
10284  // marker for only permitted additional entry being a repeat, only needed if the
10285  // finish entry is not the last entry
10286  }
10287  if(FinalCall)
10288  {
10289  // interpret & add to ActionVector
10290  TDateTime TempTime;
10291  TActionVectorEntry ActionVectorEntry;
10292  ActionVectorEntry.FormatType = FormatType;
10293  ActionVectorEntry.LocationType = LocationType;
10294  ActionVectorEntry.SequenceType = SequenceType;
10295  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10296  ActionVectorEntry.Warning = Warning;
10297  if(FormatType == TimeLoc)
10298  {
10299  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10300  {
10301  ;
10302  } // these will all be true as final call
10303 
10304  ActionVectorEntry.LocationName = Second;
10305  }
10306  else if(FormatType == PassTime) // new
10307  {
10308  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10309  {
10310  ;
10311  }
10312  ActionVectorEntry.Command = Second;
10313  ActionVectorEntry.LocationName = Third;
10314  }
10315  else if(FormatType == TimeTimeLoc)
10316  {
10317  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10318  {
10319  ;
10320  }
10321  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10322  {
10323  ;
10324  }
10325  ActionVectorEntry.LocationName = Third;
10326  }
10327  else if(FormatType == TimeCmd)
10328  {
10329  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10330  {
10331  ;
10332  }
10333  ActionVectorEntry.Command = Second;
10334  }
10335  else if(FormatType == ExitRailway)
10336  {
10337  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10338  {
10339  ;
10340  }
10341  ActionVectorEntry.Command = Second;
10342  ActionVectorEntry.ExitList = ExitList;
10343  }
10344  else if(FormatType == StartNew)
10345  {
10346  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10347  {
10348  ;
10349  }
10350  ActionVectorEntry.Command = Second;
10351  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10352  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10353  if(Fourth == 'S')
10354  {
10355  ActionVectorEntry.SignallerControl = true;
10356  }
10357  }
10358  else if(FormatType == SNTShuttle)
10359  {
10360  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10361  {
10362  ;
10363  }
10364  ActionVectorEntry.Command = Second;
10365  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10366  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10367  ActionVectorEntry.OtherHeadCode = Fourth;
10368  }
10369  else if(FormatType == SNSShuttle)
10370  {
10371  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10372  {
10373  ;
10374  }
10375  ActionVectorEntry.Command = Second;
10376  ActionVectorEntry.OtherHeadCode = Third;
10377  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10378  }
10379  else if(FormatType == TimeCmdHeadCode)
10380  {
10381  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10382  {
10383  ;
10384  }
10385  ActionVectorEntry.Command = Second;
10386  ActionVectorEntry.OtherHeadCode = Third;
10387  }
10388  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10389  {
10390  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10391  {
10392  ;
10393  }
10394  ActionVectorEntry.Command = Second;
10395  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10396  }
10397  else if(FormatType == FSHNewService)
10398  {
10399  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10400  {
10401  ;
10402  }
10403  ActionVectorEntry.Command = Second;
10404  ActionVectorEntry.OtherHeadCode = Third;
10405  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10406  }
10407  else if(FormatType == FinRemHere)
10408  {
10409  ActionVectorEntry.Command = Second;
10410  }
10411  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10412  }
10413  }
10414  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10415  {
10416  OneEntry = NewRemainder;
10417  First = "";
10418  Second = "";
10419  Third = "";
10420  Fourth = "";
10421  RearStartOrRepeatMins = 0;
10422  FrontStartOrRepeatDigits = 0;
10423  NumberOfRepeats = 0;
10424  if((FinishFlag) && (OneEntry[1] != 'R'))
10425  // already had a finish entry
10426  {
10427  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10428  Utilities->CallLogPop(79);
10429  return(false);
10430  }
10431  if(OneEntry[1] != 'R') // must be finish
10432  {
10433  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10434  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10435  {
10436  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10437  Utilities->CallLogPop(757);
10438  return(false);
10439  }
10440  if(SequenceType != Finish)
10441  {
10442  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10443  Utilities->CallLogPop(785);
10444  return(false);
10445  }
10446  if(FinalCall)
10447  {
10448  // interpret & add to ActionVector
10449  TDateTime TempTime;
10450  TActionVectorEntry ActionVectorEntry;
10451  ActionVectorEntry.FormatType = FormatType;
10452  ActionVectorEntry.LocationType = LocationType;
10453  ActionVectorEntry.SequenceType = SequenceType;
10454  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10455  ActionVectorEntry.Warning = Warning;
10456  if(FormatType == TimeCmd)
10457  {
10458  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10459  {
10460  ;
10461  }
10462  ActionVectorEntry.Command = Second;
10463  }
10464  else if(FormatType == TimeCmdHeadCode)
10465  {
10466  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10467  {
10468  ;
10469  }
10470  ActionVectorEntry.Command = Second;
10471  ActionVectorEntry.OtherHeadCode = Third;
10472  }
10473  else if(FormatType == FNSNonRepeatToShuttle)
10474  {
10475  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10476  {
10477  ;
10478  }
10479  ActionVectorEntry.Command = Second;
10480  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10481  }
10482  else if(FormatType == FSHNewService)
10483  {
10484  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10485  {
10486  ;
10487  }
10488  ActionVectorEntry.Command = Second;
10489  ActionVectorEntry.OtherHeadCode = Third;
10490  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10491  }
10492  else if(FormatType == ExitRailway)
10493  {
10494  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10495  {
10496  ;
10497  }
10498  ActionVectorEntry.Command = Second;
10499  ActionVectorEntry.ExitList = ExitList;
10500  }
10501  else if(FormatType == FinRemHere)
10502  {
10503  ActionVectorEntry.Command = Second;
10504  }
10505  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10506  }
10507  }
10508  else // repeat
10509  {
10510  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10511  {
10512  Utilities->CallLogPop(786);
10513  // error messages given in SplitRepeat
10514  return(false);
10515  }
10516  if(FinalCall)
10517  {
10518  TActionVectorEntry ActionVectorEntry;
10519  ActionVectorEntry.FormatType = Repeat;
10520  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10521  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
10522  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
10523  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10524  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10525  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
10526  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10527  }
10528  }
10529  }
10530  }
10531  if(FinalCall)
10532  {
10533  TrainDataVector.push_back(TempTrainDataEntry);
10534  }
10535  }
10536  Utilities->CallLogPop(80);
10537  return(true);
10538 }
10539 
10540 // ---------------------------------------------------------------------------
10541 
10542 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
10543 {
10544  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
10545  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
10546  {
10547  Utilities->CallLogPop(1890);
10548  return(false);
10549  }
10550  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
10551  {
10552  Utilities->CallLogPop(1891);
10553  return(false);
10554  }
10555  Utilities->CallLogPop(1892);
10556  return(true);
10557 }
10558 
10559 // ---------------------------------------------------------------------------
10560 
10561 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
10562 // 1st 5 chars must be HH:MM, anything else will be ignored
10563 {
10564  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
10565  if(TimeStr.Length() < 5)
10566  {
10567  Utilities->CallLogPop(926);
10568  return(false);
10569  }
10570  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
10571  {
10572  Utilities->CallLogPop(927);
10573  return(false);
10574  }
10575  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
10576  {
10577  Utilities->CallLogPop(928);
10578  return(false);
10579  }
10580  if(TimeStr[3] != ':')
10581  {
10582  Utilities->CallLogPop(929);
10583  return(false);
10584  }
10585  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
10586  {
10587  Utilities->CallLogPop(930);
10588  return(false);
10589  }
10590  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
10591  {
10592  Utilities->CallLogPop(931);
10593  return(false);
10594  }
10595  while(TimeStr.Length() > 5)
10596  {
10597  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
10598  }
10599  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
10600  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
10601 
10602  if((WholeHours + FracHour) >= 95.98334)
10603  {
10604  Utilities->CallLogPop(1817);
10605  return(false); // > 95h 59m
10606  }
10607  Time = TDateTime((WholeHours + FracHour) / 24);
10608  Utilities->CallLogPop(932);
10609  return(true);
10610 }
10611 
10612 // ---------------------------------------------------------------------------
10613 
10614 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
10615  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
10616  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
10617 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
10618  Return false for failure.
10619  See description above under ProcessOneTimetableLinefor details of train action entries
10620  NB all types set except LocationType for Sns as may be located or not
10621 */{
10622  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
10623  Warning = false;
10624  TDateTime TempTime;
10625 
10626  if(OneEntry.Length() > 0)
10627  {
10628  if(OneEntry[1] == 'W') // warning
10629  {
10630  Warning = true;
10631  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
10632  // strip it off
10633  }
10634  }
10635  if(OneEntry == "Frh")
10636  {
10637  FormatType = FinRemHere;
10638  SequenceType = Finish;
10639  LocationType = AtLocation;
10640  ShuttleLinkType = NotAShuttleLink;
10641  Second = "Frh";
10642  Utilities->CallLogPop(1016);
10643  return(true);
10644  }
10645  if(OneEntry.Length() < 7)
10646  {
10647  Utilities->CallLogPop(907);
10648  return(false); // 'HH:MM;' + at least a one-letter location name
10649  }
10650  int Pos = OneEntry.Pos(';'); // first segment delimiter
10651 
10652  if(Pos != 6)
10653  {
10654  Utilities->CallLogPop(908);
10655  return(false);
10656  // no delimiter or delimiter not in position 6, has to be a time so fail
10657  }
10658  First = OneEntry.SubString(1, 5); // has to be a time
10659  if(!CheckTimeValidity(16, First, TempTime))
10660  {
10661  Utilities->CallLogPop(909);
10662  return(false);
10663  }
10664  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
10665 
10666  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
10667  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
10668  {
10669  if(Remainder.Length() < 7)
10670  {
10671  Utilities->CallLogPop(910);
10672  return(false); // 'HH:MM;' + at least a one-letter location name
10673  }
10674  Pos = Remainder.Pos(';'); // second segment delimiter
10675  if(Pos == 0)
10676  {
10677  Utilities->CallLogPop(911);
10678  return(false);
10679  // no delimiter, has to be one between departure time & location
10680  }
10681  Second = Remainder.SubString(1, 5); // has to be a time
10682  if(!CheckTimeValidity(15, Second, TempTime))
10683  {
10684  Utilities->CallLogPop(912);
10685  return(false);
10686  }
10687  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10688  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
10689  {
10690  Utilities->CallLogPop(913);
10691  return(false);
10692  }
10693  FormatType = TimeTimeLoc;
10694  SequenceType = Intermediate;
10695  LocationType = AtLocation;
10696  ShuttleLinkType = NotAShuttleLink;
10697  Utilities->CallLogPop(914);
10698  return(true);
10699  }
10700  Pos = Remainder.Pos(';'); // second segment delimiter
10701  if(Pos == 0) // no third segment so second must be a location, or cdt
10702  {
10703  Second = Remainder;
10704  if(Second == "cdt")
10705  {
10706  FormatType = TimeCmd;
10707  ShuttleLinkType = NotAShuttleLink;
10708  LocationType = AtLocation;
10709  SequenceType = Intermediate;
10710  Utilities->CallLogPop(915);
10711  return(true);
10712  }
10713  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
10714  {
10715  Utilities->CallLogPop(916);
10716  return(false);
10717  }
10718  else
10719  {
10720  FormatType = TimeLoc;
10721  LocationType = AtLocation;
10722  SequenceType = Intermediate;
10723  ShuttleLinkType = NotAShuttleLink;
10724  Utilities->CallLogPop(917);
10725  return(true);
10726  }
10727  }
10728  // here if second segment is a command, with a third & maybe fourth segments as details
10729  if((Pos != 4) && (Pos != 7) && (Pos != 8))
10730  {
10731  Utilities->CallLogPop(918);
10732  return(false);
10733  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
10734  }
10735  Second = Remainder.SubString(1, Pos - 1); // command
10736 
10737  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10738  // details
10739  Pos = Remainder.Pos(';'); // third segment delimiter
10740  if(Pos == 0)
10741  {
10742  Third = Remainder;
10743  }
10744  else
10745  {
10746  Third = Remainder.SubString(1, Pos - 1);
10747  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10748  }
10749  if((Second == "Snt") || (Second == "Snt-sh"))
10750  // third has to be 2 element idents with a space between
10751  {
10752  int SpacePos = Third.Pos(' ');
10753  if(SpacePos == 0)
10754  {
10755  Utilities->CallLogPop(919);
10756  return(false); // no space
10757  }
10758  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
10759  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
10760  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
10761  if(CheckLocationsExistInRailway)
10762  {
10763  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
10764  {
10765  Utilities->CallLogPop(920);
10766  return(false);
10767  }
10768  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
10769  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
10770  }
10771  if(Second == "Snt")
10772  {
10773  FormatType = StartNew;
10774  SequenceType = Start;
10775  LocationType = NoLocation;
10776  // can't be set until know whether located or not - done in SecondPassActions
10777  ShuttleLinkType = NotAShuttleLink;
10778  }
10779  else // Snt-sh
10780  {
10781  FormatType = SNTShuttle;
10782  LocationType = AtLocation;
10783  SequenceType = Start;
10784  ShuttleLinkType = ShuttleLink;
10785  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10786  {
10787  Utilities->CallLogPop(1038);
10788  return(false);
10789  }
10790  }
10791  Utilities->CallLogPop(921);
10792  return(true);
10793  }
10794  if(Second == "Sns-sh") // third & fourth have to be headcodes
10795  {
10796  FormatType = SNSShuttle;
10797  LocationType = AtLocation;
10798  SequenceType = Start;
10799  ShuttleLinkType = ShuttleLink;
10800  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10801  {
10802  Utilities->CallLogPop(1039);
10803  return(false);
10804  }
10805  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10806  {
10807  Utilities->CallLogPop(1040);
10808  return(false);
10809  }
10810  Utilities->CallLogPop(1041);
10811  return(true);
10812  }
10813  if(Second == "F-nshs")
10814  {
10815  FormatType = FNSNonRepeatToShuttle;
10816  LocationType = AtLocation;
10817  SequenceType = Finish;
10818  ShuttleLinkType = ShuttleLink;
10819  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10820  {
10821  Utilities->CallLogPop(1047);
10822  return(false);
10823  }
10824  Utilities->CallLogPop(1048);
10825  return(true);
10826  }
10827  if(Second == "Sns-fsh")
10828  {
10829  FormatType = SNSNonRepeatFromShuttle;
10830  LocationType = AtLocation;
10831  SequenceType = Start;
10832  ShuttleLinkType = ShuttleLink;
10833  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10834  {
10835  Utilities->CallLogPop(1098);
10836  return(false);
10837  }
10838  Utilities->CallLogPop(1099);
10839  return(true);
10840  }
10841  if(Second == "Fns-sh") // third & fourth have to be headcodes
10842  {
10843  FormatType = FSHNewService;
10844  LocationType = AtLocation;
10845  SequenceType = Finish;
10846  ShuttleLinkType = ShuttleLink;
10847  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10848  {
10849  Utilities->CallLogPop(1050);
10850  return(false);
10851  }
10852  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10853  {
10854  Utilities->CallLogPop(1051);
10855  return(false);
10856  }
10857  Utilities->CallLogPop(1052);
10858  return(true);
10859  }
10860  // new segment for 'pas'
10861  if(Second == "pas") // third has to be a location
10862  {
10863  FormatType = PassTime;
10864  LocationType = EnRoute;
10865  SequenceType = Intermediate;
10866  ShuttleLinkType = NotAShuttleLink;
10867  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10868  {
10869  Utilities->CallLogPop(1515);
10870  return(false);
10871  }
10872  Utilities->CallLogPop(1516);
10873  return(true);
10874  }
10875  // new segment for revised 'Fer'
10876  if(Second == "Fer")
10877  // third has to be a set of IDs separated by spaces, and at least 1
10878  {
10879  FormatType = ExitRailway;
10880  LocationType = EnRoute;
10881  SequenceType = Finish;
10882  ShuttleLinkType = NotAShuttleLink;
10883  if(CheckLocationsExistInRailway)
10884  {
10885  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10886  {
10887  Utilities->CallLogPop(1519);
10888  return(false);
10889  }
10890  }
10891  Utilities->CallLogPop(1520);
10892  return(true);
10893  }
10894  // all remainder must be TimeCmdHeadCode types to be valid
10895  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10896  (Second != "Frh-sh"))
10897  {
10898  Utilities->CallLogPop(922);
10899  return(false); // all TimeCmdHeadCode types
10900  }
10901  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10902  {
10903  Utilities->CallLogPop(923);
10904  return(false);
10905  }
10906  FormatType = TimeCmdHeadCode;
10907  LocationType = AtLocation;
10908  if(Second == "Frh-sh")
10909  {
10910  ShuttleLinkType = ShuttleLink;
10911  }
10912  else
10913  {
10914  ShuttleLinkType = NotAShuttleLink;
10915  }
10916  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10917  {
10918  SequenceType = Finish;
10919  }
10920  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10921  {
10922  SequenceType = Intermediate;
10923  }
10924  if((Second == "Sfs") || (Second == "Sns"))
10925  {
10926  SequenceType = Start;
10927  }
10928  Utilities->CallLogPop(924);
10929  return(true);
10930 }
10931 
10932 // ---------------------------------------------------------------------------
10933 
10934 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10935 {
10936  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10937  // and contains no special characters
10938  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10939  if(LocStr == "")
10940  {
10941  Utilities->CallLogPop(1353);
10942  return(false); // has to have at least one character
10943  }
10944  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10945  {
10946  Utilities->CallLogPop(1354);
10947  return(false); // can't begin with a number
10948  }
10949  for(int x = 1; x < LocStr.Length() + 1; x++)
10950  {
10951  if(LocStr[x] < ' ')
10952  {
10953  Utilities->CallLogPop(1355);
10954  return(false); // contains a special character
10955  }
10956  if(LocStr[x] > 'z')
10957  {
10958  Utilities->CallLogPop(1356);
10959  return(false); // contains a character outside the standard ASCII set
10960  }
10961  }
10962  // check exists in railway location list if CheckLocationsExistInRailway is true
10963  if(CheckLocationsExistInRailway)
10964  {
10965  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10966  {
10967  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10968  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
10969  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
10970  "that includes a continuation will not be valid.");
10971  Utilities->CallLogPop(1357);
10972  return(false);
10973  }
10974  }
10975  Utilities->CallLogPop(1358);
10976  return(true);
10977 }
10978 
10979 // ---------------------------------------------------------------------------
10980 
10981 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
10982 {
10983  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
10984  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
10985  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
10986  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
10987  HeadCode);
10988  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
10989  {
10990  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
10991  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Snt-sh, Sns-sh, Fns-sh or Frh-sh");
10992  Utilities->CallLogPop(1359);
10993  return(false);
10994  }
10995  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
10996  for(int x = 1; x < (HeadCode.Length() + 1); x++)
10997  {
10998  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
10999  {
11000  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11001  Utilities->CallLogPop(1895);
11002  return(false);
11003  }
11004  }
11005  // secondly ensure the true Headcode only has letters or digits
11006  for(int x = 3; x >= 0; x--)
11007  {
11008  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11009  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11010  {
11011  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11012  Utilities->CallLogPop(1790);
11013  return(false);
11014  }
11015  }
11016  Utilities->CallLogPop(1364);
11017  return(true);
11018 }
11019 
11020 // ---------------------------------------------------------------------------
11021 
11022 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
11023 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11024 {
11025  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11026  ExitList.clear();
11027  AnsiString CurrentID = "";
11028 
11029  if(IDSet.Length() == 0)
11030  {
11031  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11032  Utilities->CallLogPop(1521);
11033  return(false);
11034  }
11035  for(int x = 1; x <= IDSet.Length(); x++)
11036  {
11037  char C = IDSet[x];
11038  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11039  {
11040  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11041  Utilities->CallLogPop(1522);
11042  return(false);
11043  }
11044  }
11045  int Pos = IDSet.Pos(' '); // look for the first space
11046 
11047  while(true)
11048  {
11049  if(Pos == 0)
11050  {
11051  CurrentID = IDSet;
11052  IDSet = "";
11053  }
11054  else
11055  {
11056  CurrentID = IDSet.SubString(1, Pos - 1);
11057  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11058  }
11059  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11060  if(VecPos == -1)
11061  {
11062  Utilities->CallLogPop(1523);
11063  return(false); // messages given in GetTrackVectorPositionFromString
11064  }
11065  else
11066  {
11067  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11068  {
11069  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11070  Utilities->CallLogPop(1524);
11071  return(false);
11072  }
11073  else
11074  {
11075  // first check for duplicates
11076  if(!ExitList.empty())
11077  {
11078  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11079  {
11080  if(*ELIT == VecPos)
11081  {
11082  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11083  Utilities->CallLogPop(1532);
11084  return(false);
11085  }
11086  }
11087  }
11088  // of OK add it to the list
11089  ExitList.push_back(VecPos);
11090  }
11091  }
11092  if(IDSet == "")
11093  {
11094  Utilities->CallLogPop(1525);
11095  return(true);
11096  }
11097  else
11098  {
11099  Pos = IDSet.Pos(' '); // look for the next space
11100  }
11101  } // while(true)
11102 }
11103 
11104 // ---------------------------------------------------------------------------
11105 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11106  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11107 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11108 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11109 // of each item
11110 {
11111  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11112  int Pos = 0;
11113  AnsiString Remainder = "";
11114  int SemiColonCount = 0;
11115 
11116  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11117  {
11118  if(TrainInfoStr[x] == ';')
11119  {
11120  SemiColonCount++;
11121  }
11122  }
11123  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11124  {
11125  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11126  "'. Should be headcode + optional description for a continuing service;" +
11127  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11128  Utilities->CallLogPop(880);
11129  return(false);
11130  }
11131  if(SemiColonCount == 0)
11132  {
11133  HeadCode = TrainInfoStr;
11134  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11135  {
11136  Utilities->CallLogPop(881);
11137  return(false);
11138  }
11139  Utilities->CallLogPop(882);
11140  return(true);
11141  }
11142  if(SemiColonCount == 1) // headcode & description only
11143  {
11144  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11145  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11146  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11147  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11148  {
11149  Utilities->CallLogPop(883);
11150  return(false);
11151  }
11152  if(Description == "")
11153  {
11154  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11155  Utilities->CallLogPop(884);
11156  return(false);
11157  }
11158  if(Description.Length() > 60)
11159  {
11160  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11161  Utilities->CallLogPop(1157);
11162  return(false);
11163  }
11164  for(int x = 1; x < Description.Length() + 1; x++)
11165  {
11166  if((Description[x] < ' ') || (Description[x] > '~'))
11167  {
11168  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11169  Utilities->CallLogPop(885);
11170  return(false);
11171  }
11172  }
11173  Utilities->CallLogPop(886);
11174  return(true);
11175  }
11176  // if here must have 6 or 7 semicolons
11177  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11178  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11179  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11180  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11181  {
11182  Utilities->CallLogPop(887);
11183  return(false);
11184  }
11185  Pos = Remainder.Pos(';'); // 2nd delimiter
11186  Description = Remainder.SubString(1, Pos - 1);
11187  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11188  if(Description == "")
11189  {
11190  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11191  Utilities->CallLogPop(888);
11192  return(false);
11193  }
11194  if(Description.Length() > 60)
11195  {
11196  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11197  Utilities->CallLogPop(1158);
11198  return(false);
11199  }
11200  for(int x = 1; x < Description.Length() + 1; x++)
11201  {
11202  if((Description[x] < ' ') || (Description[x] > 126))
11203  {
11204  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11205  Utilities->CallLogPop(889);
11206  return(false);
11207  }
11208  }
11209  Pos = Remainder.Pos(';'); // 3rd delimiter
11210  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11211 
11212  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11213  if(StartSpeedStr == "")
11214  {
11215  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11216  Utilities->CallLogPop(890);
11217  return(false);
11218  }
11219  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11220  {
11221  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11222  {
11223  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11224  Utilities->CallLogPop(891);
11225  return(false);
11226  }
11227  }
11228  StartSpeed = StartSpeedStr.ToInt();
11229  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11230  {
11231  StartSpeed = TTrain::MaximumSpeedLimit;
11232  if(!SSHigh) // added at v2.4.0
11233  {
11234  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11235  SSHigh = true;
11236  }
11237  }
11238  Pos = Remainder.Pos(';'); // 4th delimiter
11239  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11240 
11241  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11242  if(MaxRunningSpeedStr == "")
11243  {
11244  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11245  Utilities->CallLogPop(892);
11246  return(false);
11247  }
11248  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11249  {
11250  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11251  {
11252  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11253  Utilities->CallLogPop(893);
11254  return(false);
11255  }
11256  }
11257  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11258  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11259  {
11260  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11261  if(!MRSHigh) // added at v2.4.0
11262  {
11263  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11264  MRSHigh = true;
11265  }
11266  }
11267  if(MaxRunningSpeed < 10)
11268  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11269  {
11270  MaxRunningSpeed = 10;
11271  if(!MRSLow) // added at v2.4.0
11272  {
11273  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11274  MRSLow = true;
11275  }
11276  }
11277  Pos = Remainder.Pos(';'); // 5th delimiter
11278  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11279 
11280  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11281  if(MassStr == "")
11282  {
11283  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11284  Utilities->CallLogPop(895);
11285  return(false);
11286  }
11287  for(int x = 1; x < MassStr.Length() + 1; x++)
11288  {
11289  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11290  {
11291  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11292  Utilities->CallLogPop(896);
11293  return(false);
11294  }
11295  }
11296  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11297  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11298  {
11299  Mass = TTrain::MaximumMassLimit;
11300  if(!MassHigh) // added at v2.4.0
11301  {
11302  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11303  MassHigh = true;
11304  }
11305  }
11306  if(Mass == 0)
11307  {
11308  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11309  Utilities->CallLogPop(897);
11310  return(false);
11311  }
11312  Pos = Remainder.Pos(';'); // 6th delimiter
11313  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11314 
11315  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11316  if(MaxBrakeForceStr == "")
11317  {
11318  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11319  Utilities->CallLogPop(898);
11320  return(false);
11321  }
11322  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11323  {
11324  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11325  {
11326  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11327  Utilities->CallLogPop(899);
11328  return(false);
11329  }
11330  }
11331  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11332 
11333  // convert to kg force
11334  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11335  {
11336  MaxBrakeForce = Mass;
11337  if(!BFHigh) // added at v2.4.0
11338  {
11339  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11340  BFHigh = true;
11341  }
11342  }
11343  if((MaxBrakeForce / Mass) < 0.01)
11344  {
11345  MaxBrakeForce = Mass * 0.01;
11346  if(!BFLow) // added at v2.4.0
11347  {
11348  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11349  BFLow = true;
11350  }
11351  }
11352  // convert to m/s/s
11353  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11354  // now may have just a power entry or power and signaller max. speed
11355  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11356 
11357  if(SemiColonCount == 6)
11358  {
11359  GrossPowerStr = Remainder;
11360  SignallerSpeedStr = "30"; // default value
11361  }
11362  else // must be 7
11363  {
11364  Pos = Remainder.Pos(';'); // 7th delimiter
11365  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11366  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11367  }
11368  // deal with GrossPower
11369  if(GrossPowerStr == "")
11370  {
11371  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11372  Utilities->CallLogPop(901);
11373  return(false);
11374  }
11375  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11376  {
11377  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11378  {
11379  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11380  Utilities->CallLogPop(902);
11381  return(false);
11382  }
11383  }
11384 
11385  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11386 
11387  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11388  {
11389  GrossPower = TTrain::MaximumPowerLimit;
11390  if(!PwrHigh)
11391  {
11392  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11393  PwrHigh = true;
11394  }
11395  }
11396  else if(GrossPower == 0) // changed at v2.4.0
11397  {
11398  GrossPower = 0.1;
11399  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11400  }
11401  else if((GrossPower > 0) && (GrossPower < 10000))
11402  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11403  {
11404  GrossPower = 10000;
11405  }
11406  PowerAtRail = GrossPower * 0.8;
11407  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11408 
11409  // deal with SignallerSpeed
11410  if(SignallerSpeedStr == "")
11411  {
11412  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11413  Utilities->CallLogPop(1771);
11414  return(false);
11415  }
11416  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11417  {
11418  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11419  {
11420  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11421  Utilities->CallLogPop(1769);
11422  return(false);
11423  }
11424  }
11425  SignallerSpeed = SignallerSpeedStr.ToInt();
11426  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11427  {
11428  SignallerSpeed = TTrain::MaximumSpeedLimit;
11429  if(!SigSHigh)
11430  {
11431  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11432  SigSHigh = true;
11433  }
11434  }
11435  if(SignallerSpeed < 10)
11436  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11437  {
11438  SignallerSpeed = 10;
11439  if(!SigSLow)
11440  {
11441  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11442  SigSLow = true;
11443  }
11444  // Utilities->CallLogPop(1770);
11445  // return false;
11446  }
11447  Utilities->CallLogPop(904);
11448  return(true);
11449 }
11450 
11451 // ---------------------------------------------------------------------------
11452 
11453 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11454  bool GiveMessages)
11455 {
11456  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11457  // function checks validity of each item and returns false for error
11458  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11459  if(OneEntry.Length() < 7)
11460  {
11461  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11462  Utilities->CallLogPop(865);
11463  return(false);
11464  }
11465  int SemiColonCount = 0;
11466 
11467  for(int x = 1; x < OneEntry.Length() + 1; x++)
11468  {
11469  if(OneEntry[x] == ';')
11470  {
11471  SemiColonCount++;
11472  }
11473  }
11474  if(SemiColonCount != 3)
11475  {
11476  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11477  Utilities->CallLogPop(866);
11478  return(false);
11479  }
11480  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11481  {
11482  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11483  Utilities->CallLogPop(867);
11484  return(false);
11485  }
11486  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11487  // strip off R;
11488 
11489  int Pos = 0;
11490 
11491  Pos = Remainder.Pos(';');
11492  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11493 
11494  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11495  if(MinutesStr == "")
11496  {
11497  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11498  Utilities->CallLogPop(868);
11499  return(false);
11500  }
11501  if(MinutesStr.Length() > 3)
11502  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11503  {
11504  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11505  Utilities->CallLogPop(2119);
11506  return(false);
11507  }
11508  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11509  {
11510  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11511  {
11512  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11513  Utilities->CallLogPop(869);
11514  return(false);
11515  }
11516  }
11517  RearStartOrRepeatMins = MinutesStr.ToInt();
11518  if(RearStartOrRepeatMins == 0)
11519  {
11520  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11521  Utilities->CallLogPop(870);
11522  return(false);
11523  }
11524  Pos = Remainder.Pos(';');
11525  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
11526 
11527  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11528  if(DigitsStr == "")
11529  {
11530  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
11531  Utilities->CallLogPop(871);
11532  return(false);
11533  }
11534  for(int x = 1; x < DigitsStr.Length() + 1; x++)
11535  {
11536  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
11537  {
11538  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
11539  Utilities->CallLogPop(872);
11540  return(false);
11541  }
11542  }
11543  if(DigitsStr.Length() > 2)
11544  {
11545  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
11546  Utilities->CallLogPop(873);
11547  return(false);
11548  }
11549  FrontStartOrRepeatDigits = DigitsStr.ToInt();
11550 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
11551  route rather than the service
11552  if(FrontStartOrRepeatDigits == 0)
11553  {
11554  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
11555  Utilities->CallLogPop(874);
11556  return false;
11557  }
11558 */
11559  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
11560  // new for v0.6b for unrestricted headcodes
11561  {
11562  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
11563  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
11564  Utilities->CallLogPop(1889);
11565  return(false);
11566  }
11567  AnsiString NumberStr = Remainder;
11568 
11569  if(NumberStr == "")
11570  {
11571  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
11572  Utilities->CallLogPop(875);
11573  return(false);
11574  }
11575  if(NumberStr.Length() > 4)
11576  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
11577  {
11578  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
11579  Utilities->CallLogPop(2118);
11580  return(false);
11581  }
11582  for(int x = 1; x < NumberStr.Length() + 1; x++)
11583  {
11584  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
11585  // catches negative numbers
11586  {
11587  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
11588  Utilities->CallLogPop(876);
11589  return(false);
11590  }
11591  }
11592  NumberOfRepeats = NumberStr.ToInt();
11593  if(NumberOfRepeats == 0)
11594  {
11595  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
11596  Utilities->CallLogPop(877);
11597  return(false);
11598  }
11599  Utilities->CallLogPop(878);
11600  return(true);
11601 }
11602 
11603 // ---------------------------------------------------------------------------
11604 
11605 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages)
11606 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
11607  vector rather than the timetable
11608  Note also that for unlocated Snt entries the LocationType hasn't yet been set
11609 
11610  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
11611 
11612  For info:-
11613  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
11614  {
11615  public:
11616  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
11618  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
11619  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
11620  int NumberOfRepeats; ///< the number of repeating services
11621  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
11623  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
11625  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
11626  TTimetableFormatType FormatType; ///< defines the timetable action type
11627  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
11628  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
11629  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
11630  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
11632  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
11634 
11635  // inline function
11636 
11638  TActionVectorEntry() {
11639  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
11640  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
11641  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
11642  Warning = false; SignallerControl = false;
11643  }
11644  };
11645 
11646  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
11647 
11648  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
11649  {
11650  public:
11651  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
11654  double MaxBrakeRate; ///< in metres/sec/sec
11655  double MaxRunningSpeed; ///< in km/h
11656  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
11657  int Mass; ///< in kg
11658  int NumberOfTrains; ///< number of repeats + 1
11659  int SignallerSpeed; ///< in km/h for use when under signaller control
11660  int StartSpeed; ///< in km/h
11661  TActionVector ActionVector; ///< all the actions for the train
11662  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
11663 
11664  //inline function
11665 
11667  TTrainDataEntry()
11668  {
11669  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
11670  }
11671  };
11672 
11673  Allowable successors:-
11674  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
11675  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11676  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
11677  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11678  set location, else fails)
11679  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11680  set location, else fails)
11681  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11682  set location, else fails)
11683  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
11684  set location, else fails)
11685  Fns -> R only
11686  F-nshs -> Nothing (no repeats permitted)
11687  Fjo -> R only
11688  Frh -> R only
11689  Fer -> R only
11690  Frh-sh -> R only
11691  Fns-sh -> R only
11692  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
11693  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11694  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11695  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11696  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
11697  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11698  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11699  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
11700  Repeat -> Nothing
11701 
11702  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
11703  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
11704  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
11705  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
11706  Check all times increase or stay same through ActionVector
11707  Cycle through all entries in vector setting arr & dep times based on above list
11708  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
11709  Check locations match the arr & dep TimeLoc entries
11710  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
11711  Make above valid succession checks
11712  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
11713  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
11714  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11715  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
11716  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
11717  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11718  Set train info for Sfs & Sns entries
11719  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
11720  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
11721  element at each end, or length of 3 & 1 extra element at either end
11722  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
11723  Check all Cmds have EventTime set & Arr & Dep times = -1
11724  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
11725  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
11726 
11727  Give messages in function if errors detected and clear the vector. Return false for failure.
11728 */
11729 
11730 /* Earlier checks:-
11731  Checks carried out with error messages in this function:-
11732  At least one comma in the line (it's based on a csv file);
11733  No entries following train information;
11734  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11735  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11736  First entry not a start entry;
11737  Train information incomplete before a start entry;
11738  Entry follows a finish entry but doesn't begin with 'R';
11739  SplitEntry returns false in a finish entry - message repeats the entry for information;
11740  Last action entry isn't a finish entry.
11741 
11742  Function returns false with no message if:-
11743  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11744  time is found at all then an error message is given in the calling function);
11745  SplitTrainInfo returns false (message given in called function);
11746  SplitRepeat returns false (message given in called function).
11747 */{
11748  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
11749  if(TrainDataVector.empty())
11750  {
11751  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
11752  TrainDataVector.clear();
11753  Utilities->CallLogPop(1832);
11754  return(false);
11755  }
11756 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
11757  1) must have at least one actionvector entry
11758  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
11759  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
11760  4) first entry must be a start;
11761  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
11762  5) a start must be the first entry;
11763  6) a repeat entry must be the last;
11764  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
11765  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
11766  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
11767 */
11768 
11769  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
11770  {
11771  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11772  if(TrainDataVector.at(x).ActionVector.empty())
11773  {
11774  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
11775  TrainDataVector.clear();
11776  Utilities->CallLogPop(1833);
11777  return(false);
11778  }
11779  }
11780  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11781  {
11782  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11783  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11784  if(!(AVEntry0.SignallerControl))
11785  {
11786  if(TrainDataVector.at(x).ActionVector.size() == 1)
11787  {
11788  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11789  TrainDataVector.clear();
11790  Utilities->CallLogPop(1822);
11791  return(false);
11792  }
11793  }
11794  }
11795  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11796  {
11797  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11798  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11799  if(AVEntry0.SignallerControl)
11800  {
11801  if(TrainDataVector.at(x).ActionVector.size() > 2)
11802  {
11803  SecondPassMessage(GiveMessages,
11804  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11805  TDEntry.HeadCode);
11806  TrainDataVector.clear();
11807  Utilities->CallLogPop(1837);
11808  return(false);
11809  }
11810  if(TrainDataVector.at(x).ActionVector.size() > 1)
11811  {
11812  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11813  if(AVEntry1.FormatType != Repeat)
11814  {
11815  SecondPassMessage(GiveMessages,
11816  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11817  TrainDataVector.clear();
11818  Utilities->CallLogPop(1838);
11819  return(false);
11820  }
11821  }
11822  }
11823  }
11824  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11825  {
11826  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11827  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11828  if(AVEntry0.SequenceType != Start)
11829  {
11830  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11831  TrainDataVector.clear();
11832  Utilities->CallLogPop(1824);
11833  return(false);
11834  }
11835  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11836  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11837  // and others for a located Snt, but those checks done later
11838  {
11839  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11840  // must be a second entry if first not signallercontrol
11841  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11842  {
11843  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11844  TDEntry.HeadCode);
11845  TrainDataVector.clear();
11846  Utilities->CallLogPop(2046);
11847  return(false);
11848  }
11849  }
11850  }
11851  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11852  {
11853  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11854  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11855  {
11856  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11857  if((AVEntry.SequenceType == Start) && (y != 0))
11858  {
11859  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11860  TrainDataVector.clear();
11861  Utilities->CallLogPop(1825);
11862  return(false);
11863  }
11864  }
11865  }
11866  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11867  {
11868  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11869  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11870  {
11871  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11872  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11873  {
11874  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11875  TrainDataVector.clear();
11876  Utilities->CallLogPop(1826);
11877  return(false);
11878  }
11879  }
11880  }
11881  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11882  {
11883  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11884  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11885  {
11886  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11887  if((y == 0) && AVEntry.SignallerControl)
11888  {
11889  break;
11890  }
11891  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11892  {
11893  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11894  {
11895  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11896  TrainDataVector.clear();
11897  Utilities->CallLogPop(1827);
11898  return(false);
11899  }
11900  if(AVEntry.FormatType == Repeat)
11901  {
11902  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11903  if(LastAVEntry.SequenceType != Finish)
11904  {
11905  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11906  TrainDataVector.clear();
11907  Utilities->CallLogPop(1828);
11908  return(false);
11909  }
11910  }
11911  }
11912  }
11913  }
11914  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11915  {
11916  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11917  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11918  {
11919  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11920  if(AVEntry.SequenceType == Finish)
11921  {
11922  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11923  {
11924  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11925  TrainDataVector.clear();
11926  Utilities->CallLogPop(1829);
11927  return(false);
11928  }
11929  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11930  {
11931  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11932  {
11933  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11934  TDEntry.HeadCode);
11935  TrainDataVector.clear();
11936  Utilities->CallLogPop(1830);
11937  return(false);
11938  }
11939  }
11940  }
11941  }
11942  }
11943 
11944  // end of new preliminary checks
11945 
11946  // check ActionVector present and check start event successor validity
11947  // For Snt & Snt-sh set location if stopped, don't set any times yet
11948  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11949  {
11950  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11951  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11952  // use reference so can change internals where necessary
11953  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11954  {
11955  AnsiString LocationName = "";
11956  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11957  // it is at a location
11958  {
11959  if(TDEntry.StartSpeed == 0) // stopped
11960  {
11961  AVEntry0.LocationName = LocationName;
11962  AVEntry0.LocationType = AtLocation;
11963  // check successor validity for located Snt that isn't a SignallerControl entry
11964  if(!AVEntry0.SignallerControl)
11965  {
11966  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11967  // at least 2 entries present checked in integrity check so (1) valid
11968  if(!AtLocSuccessor(AVEntry1))
11969  {
11970  // Frh following Snt-sh will return false in location check, so no need to check here
11971  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
11972  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11973  TrainDataVector.clear();
11974  Utilities->CallLogPop(523);
11975  return(false);
11976  }
11977  }
11978  }
11979  else
11980  {
11981  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
11982  TDEntry.HeadCode);
11983  TrainDataVector.clear();
11984  Utilities->CallLogPop(791);
11985  return(false);
11986  }
11987  }
11988  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
11989  {
11990  if(AVEntry0.Command == "Snt-sh")
11991  {
11992  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
11993  TrainDataVector.clear();
11994  Utilities->CallLogPop(1042);
11995  return(false);
11996  }
11997  AVEntry0.LocationType = EnRoute;
11998  if(!AVEntry0.SignallerControl)
11999  {
12000  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12001  // at least 2 entries checked in integrity check so (1) valid
12002  if(!MovingSuccessor(AVEntry1))
12003  {
12004  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12005  TDEntry.HeadCode);
12006  TrainDataVector.clear();
12007  Utilities->CallLogPop(790);
12008  return(false);
12009  }
12010  }
12011  }
12012  }
12013  // check other start successors
12014  else if(AVEntry0.SequenceType == Start)
12015  {
12016  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12017  // at least 2 entries present checked in integrity check so (1) valid
12018  if(!AtLocSuccessor(AVEntry1))
12019  {
12020  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12021  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12022  TrainDataVector.clear();
12023  Utilities->CallLogPop(793);
12024  return(false);
12025  }
12026  }
12027  }
12028 
12029  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12030  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12031  {
12032  bool FoundFlag = false;
12033  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12034  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12035  // use reference so can change internals
12036  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12037  {
12038  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12039  {
12040  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12041  if(AVEntry2.FormatType == TimeLoc)
12042  {
12043  FoundFlag = true;
12044  AVEntry.LocationName = AVEntry2.LocationName;
12045  break;
12046  }
12047  }
12048  if(!FoundFlag)
12049  {
12050  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12051  TDEntry.HeadCode);
12052  TrainDataVector.clear();
12053  Utilities->CallLogPop(851);
12054  return(false);
12055  }
12056  }
12057  }
12058 
12059  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12060  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12061  {
12062  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12063  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12064  {
12065  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12066  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12067  {
12068  if(AVEntry.LocationName == "")
12069  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12070  {
12071  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12072  ": an event should have had a location name associated with it but it could not be found");
12073  TrainDataVector.clear();
12074  Utilities->CallLogPop(1831);
12075  return(false);
12076  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12077  }
12078  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12079  {
12080  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12081  // use reference so can change internals where necessary
12082  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12083  {
12084  AVEntry2.LocationName = AVEntry.LocationName;
12085  }
12086  else
12087  {
12088  break;
12089  }
12090  }
12091  }
12092  }
12093  }
12094  // all location names now set
12095 
12096  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12097  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12098  {
12099  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12100  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12101  {
12102  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12103  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12104  {
12105  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12106  // i.e at least one more, must be a repeat
12107  {
12108  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12109  {
12110  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12111  TrainDataVector.clear();
12112  Utilities->CallLogPop(798);
12113  return(false);
12114  }
12115  }
12116  }
12117  if(AVEntry.Command == "F-nshs")
12118  {
12119  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12120  // i.e has to be the last
12121  {
12122  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12123  TrainDataVector.clear();
12124  Utilities->CallLogPop(1049);
12125  return(false);
12126  }
12127  }
12128  if(AVEntry.Command == "pas")
12129  {
12130  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12131  {
12132  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12133  TrainDataVector.clear();
12134  Utilities->CallLogPop(1518);
12135  return(false);
12136  }
12137  }
12138  if(AVEntry.Command == "jbo")
12139  {
12140  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12141  {
12142  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12143  TrainDataVector.clear();
12144  Utilities->CallLogPop(800);
12145  return(false);
12146  }
12147  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12148  if(!AtLocSuccessor(AVEntry2))
12149  {
12150  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12151  ". The event isn't valid for a stationary train.");
12152  TrainDataVector.clear();
12153  Utilities->CallLogPop(801);
12154  return(false);
12155  }
12156  }
12157  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12158  {
12159  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12160  {
12161  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12162  TrainDataVector.clear();
12163  Utilities->CallLogPop(802);
12164  return(false);
12165  }
12166  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12167  if(!AtLocSuccessor(AVEntry2))
12168  {
12169  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12170  ". The event isn't valid for a stationary train.");
12171  TrainDataVector.clear();
12172  Utilities->CallLogPop(803);
12173  return(false);
12174  }
12175  }
12176  if(AVEntry.Command == "cdt")
12177  {
12178  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12179  {
12180  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12181  TrainDataVector.clear();
12182  Utilities->CallLogPop(804);
12183  return(false);
12184  }
12185  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12186  if(!AtLocSuccessor(AVEntry2))
12187  {
12188  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12189  ". The event isn't valid for a stationary train.");
12190  TrainDataVector.clear();
12191  Utilities->CallLogPop(805);
12192  return(false);
12193  }
12194  }
12195  if(AVEntry.FormatType == TimeTimeLoc)
12196  {
12197  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12198  {
12199  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12200  TrainDataVector.clear();
12201  Utilities->CallLogPop(806);
12202  return(false);
12203  }
12204  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12205  if(!MovingSuccessor(AVEntry2))
12206  {
12207  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12208  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12209  TrainDataVector.clear();
12210  Utilities->CallLogPop(807);
12211  return(false);
12212  }
12213  }
12214  if(AVEntry.FormatType == PassTime)
12215  {
12216  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12217  {
12218  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12219  TrainDataVector.clear();
12220  Utilities->CallLogPop(1530);
12221  return(false);
12222  }
12223  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12224  if(!MovingSuccessor(AVEntry2))
12225  {
12226  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12227  ". The event isn't valid for a moving train.");
12228  TrainDataVector.clear();
12229  Utilities->CallLogPop(1531);
12230  return(false);
12231  }
12232  }
12233  if(AVEntry.FormatType == Repeat)
12234  {
12235  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12236  {
12237  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12238  TrainDataVector.clear();
12239  Utilities->CallLogPop(808);
12240  return(false);
12241  }
12242  }
12243  }
12244  }
12245 
12246  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12247  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12248  {
12249  bool LastEntryIsAnArrival = false;
12250  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12251  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12252  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12253  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12254  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12255  {
12256  LastEntryIsAnArrival = false;
12257  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12258  {
12259  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12260  if(AVEntry.FormatType == TimeLoc)
12261  {
12262  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12263  {
12264  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12265  }
12266  if(LastEntryIsAnArrival)
12267  {
12268  AVEntry.DepartureTime = AVEntry.EventTime;
12269  AVEntry.EventTime = TDateTime(-1);
12270  LastEntryIsAnArrival = false;
12271  }
12272  else // last entry a departure
12273  {
12274  AVEntry.ArrivalTime = AVEntry.EventTime;
12275  AVEntry.EventTime = TDateTime(-1);
12276  LastEntryIsAnArrival = true;
12277  }
12278  }
12279  }
12280  }
12281  else // all others stopped at beginning
12282  {
12283  LastEntryIsAnArrival = true;
12284  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12285  {
12286  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12287  if(AVEntry.FormatType == TimeLoc)
12288  {
12289  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12290  {
12291  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12292  }
12293  if(LastEntryIsAnArrival)
12294  {
12295  AVEntry.DepartureTime = AVEntry.EventTime;
12296  AVEntry.EventTime = TDateTime(-1);
12297  LastEntryIsAnArrival = false;
12298  }
12299  else // last entry a departure
12300  {
12301  AVEntry.ArrivalTime = AVEntry.EventTime;
12302  AVEntry.EventTime = TDateTime(-1);
12303  LastEntryIsAnArrival = true;
12304  }
12305  }
12306  }
12307  }
12308  }
12309  // perform remaining successor checks for TimeLocs
12310  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12311  {
12312  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12313  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12314  {
12315  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12316  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12317  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12318  {
12319  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12320  {
12321  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12322  TrainDataVector.clear();
12323  Utilities->CallLogPop(809);
12324  return(false);
12325  }
12326  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12327  if(!AtLocSuccessor(AVEntry2))
12328  {
12329  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12330  ". The event isn't valid for a stationary train.");
12331  TrainDataVector.clear();
12332  Utilities->CallLogPop(810);
12333  return(false);
12334  }
12335  }
12336  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12337  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12338  {
12339  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12340  {
12341  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12342  TrainDataVector.clear();
12343  Utilities->CallLogPop(811);
12344  return(false);
12345  }
12346  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12347  if(!MovingSuccessor(AVEntry2))
12348  {
12349  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12350  ". The event isn't valid for a moving train.");
12351  TrainDataVector.clear();
12352  Utilities->CallLogPop(812);
12353  return(false);
12354  }
12355  }
12356  }
12357  }
12358 
12359  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12360  // & repeats have no times set
12361  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12362  {
12363  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12364  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12365  {
12366  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12367  if(AVEntry.FormatType == TimeLoc)
12368  {
12369  if(AVEntry.EventTime != TDateTime(-1))
12370  {
12371  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12372  }
12373  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12374  {
12375  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12376  }
12377  }
12378  if(AVEntry.FormatType == TimeTimeLoc)
12379  {
12380  if(AVEntry.EventTime != TDateTime(-1))
12381  {
12382  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12383  }
12384  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12385  {
12386  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12387  }
12388  }
12389  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12390  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12391  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12392  {
12393  if(AVEntry.EventTime == TDateTime(-1))
12394  {
12395  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12396  }
12397  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12398  {
12399  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12400  }
12401  }
12402  if(AVEntry.FormatType == Repeat)
12403  {
12404  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12405  {
12406  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12407  }
12408  }
12409  }
12410  }
12411 
12412  // check times stay same or increase, note that can have time of 0 if include midnight
12413  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12414  {
12415  TDateTime CurrentTime = TTClockTime; // the timetable start time
12416  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12417  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12418  {
12419  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12420  if(AVEntry.FormatType == Repeat)
12421  {
12422  break;
12423  }
12424  if(AVEntry.FormatType == FinRemHere)
12425  {
12426  break;
12427  }
12428  if(AVEntry.FormatType == TimeTimeLoc)
12429  {
12430  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12431  {
12432  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12433  TDEntry.HeadCode);
12434  TrainDataVector.clear();
12435  Utilities->CallLogPop(813);
12436  return(false);
12437  }
12438  if(AVEntry.ArrivalTime < CurrentTime)
12439  {
12440  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12441  TDEntry.HeadCode);
12442  TrainDataVector.clear();
12443  Utilities->CallLogPop(814);
12444  return(false);
12445  }
12446  CurrentTime = AVEntry.DepartureTime;
12447  continue;
12448  }
12449  if(AVEntry.FormatType == TimeLoc)
12450  {
12451  if(AVEntry.ArrivalTime >= TDateTime(0))
12452  {
12453  if(AVEntry.ArrivalTime < CurrentTime)
12454  {
12455  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12456  TrainDataVector.clear();
12457  Utilities->CallLogPop(815);
12458  return(false);
12459  }
12460  CurrentTime = AVEntry.ArrivalTime;
12461  }
12462  else
12463  {
12464  if(AVEntry.DepartureTime < CurrentTime)
12465  // both may be 0 legitimately so must allow for this
12466  {
12467  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12468  TrainDataVector.clear();
12469  Utilities->CallLogPop(816);
12470  return(false);
12471  }
12472  CurrentTime = AVEntry.DepartureTime;
12473  }
12474  continue;
12475  }
12476  if(AVEntry.EventTime < CurrentTime)
12477  // all others have EventTime set
12478  {
12479  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12480  ", may be before timetable start time");
12481  TrainDataVector.clear();
12482  Utilities->CallLogPop(835);
12483  return(false);
12484  }
12485  CurrentTime = AVEntry.EventTime;
12486  continue;
12487  }
12488  }
12489 
12490  // check locations consistent
12491  AnsiString LastLocationName = "";
12492 
12493  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12494  {
12495  bool LastEntryIsAnArrival = false;
12496  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12497  // first deal with moving Snt entries (all else stopped)
12498  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12499  {
12500  LastEntryIsAnArrival = false;
12501  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12502  if(LastLocationName != "")
12503  {
12504  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
12505  }
12506  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
12507  y++) // note that immediate successor to a moving Snt can only be a Moving type
12508  {
12509  // if it's a SignallerControl entry then the condition isn't met
12510  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12511  if(AVEntry.FormatType == Repeat)
12512  {
12513  break; // repeat = reached end (+allows repeat after signaller controlled entry)
12514  }
12515  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12516  {
12517  if(AVEntry.LocationName != LastLocationName)
12518  {
12519  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12520  AVEntry.Command);
12521  TrainDataVector.clear();
12522  Utilities->CallLogPop(823);
12523  return(false);
12524  }
12525  }
12526  else if(AVEntry.FormatType == TimeCmd)
12527  // cdt is the only TimeCmd
12528  {
12529  if(AVEntry.LocationName != LastLocationName)
12530  {
12531  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12532  AVEntry.Command);
12533  TrainDataVector.clear();
12534  Utilities->CallLogPop(824);
12535  return(false);
12536  }
12537  }
12538  else if(AVEntry.FormatType == TimeTimeLoc)
12539  {
12540  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12541  // last entry must be a departure or would have failed earlier
12542  {
12543  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12545 // SecondPassMessage(GiveMessages,
12546 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
12547 // TrainDataVector.clear();
12548 // Utilities->CallLogPop(825);
12549 // return false;
12550  }
12551  LastLocationName = AVEntry.LocationName;
12552  LastEntryIsAnArrival = false;
12553  }
12554  else if(AVEntry.FormatType == TimeLoc)
12555  {
12556  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12557  {
12558  SecondPassMessage(GiveMessages,
12559  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12560  TrainDataVector.clear();
12561  Utilities->CallLogPop(826);
12562  return(false);
12563  }
12564  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
12565  {
12566  SecondPassMessage(GiveMessages,
12567  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
12568  TrainDataVector.clear();
12569  Utilities->CallLogPop(827);
12570  return(false);
12571  }
12572  LastLocationName = AVEntry.LocationName;
12573  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12574  }
12575  }
12576  }
12577  else // all stationary starting entries
12578  {
12579  LastEntryIsAnArrival = true;
12580  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
12581  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12582  {
12583  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12584  if(AVEntry.FormatType == Repeat)
12585  {
12586  break;
12587  }
12588  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
12589  // no need to add anything for shuttle starts since they are at loc (0) anyway
12590  {
12591  if(AVEntry.LocationName != LastLocationName)
12592  {
12593  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12594  AVEntry.Command);
12595  TrainDataVector.clear();
12596  Utilities->CallLogPop(828);
12597  return(false);
12598  }
12599  }
12600  else if(AVEntry.FormatType == TimeCmd)
12601  // cdt is the only TimeCmd
12602  {
12603  if(AVEntry.LocationName != LastLocationName)
12604  {
12605  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
12606  AVEntry.Command);
12607  TrainDataVector.clear();
12608  Utilities->CallLogPop(829);
12609  return(false);
12610  }
12611  }
12612  else if(AVEntry.FormatType == TimeTimeLoc)
12613  {
12614  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12615  // last entry must be a departure or would have failed earlier
12616  {
12617  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12619 // SecondPassMessage(GiveMessages,
12620 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
12621 // TrainDataVector.clear();
12622 // Utilities->CallLogPop(830);
12623 // return false;
12624  }
12625  LastLocationName = AVEntry.LocationName;
12626  LastEntryIsAnArrival = false;
12627  }
12628  else if(AVEntry.FormatType == TimeLoc)
12629  {
12630  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
12631  {
12632  SecondPassMessage(GiveMessages,
12633  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
12634  TrainDataVector.clear();
12635  Utilities->CallLogPop(831);
12636  return(false);
12637  }
12638  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
12639  {
12640  SecondPassMessage(GiveMessages,
12641  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
12643 // TrainDataVector.clear();
12644 // Utilities->CallLogPop(832);
12645 // return false;
12646  }
12647  LastLocationName = AVEntry.LocationName;
12648  LastEntryIsAnArrival = !LastEntryIsAnArrival;
12649  }
12650  }
12651  }
12652  }
12653 
12654  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
12655  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
12656  AnsiString LocationNameToBeChecked = "";
12657 
12658  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12659  {
12660  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12661  unsigned int y = 0;
12662  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12663  // first discard unlocated Snt entries as they don't have location name set
12664  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12665  {
12666  y = 1;
12667  }
12668  while(y < TDEntry.ActionVector.size())
12669  // need to check each location name separately in turn, skipped for SignallerControl entries
12670  // if y == 1
12671  {
12672  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
12673  {
12674  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12675  }
12676  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
12677  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
12678  {
12679  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
12680  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
12681  {
12682  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
12683  }
12684  if(AVEntry.Command == "cdt")
12685  {
12686  break; // out of the 'z' loop since the check is only valid up to a change of direction
12687  }
12688  if(AVEntry.LocationName == LocationNameToBeChecked)
12689  {
12690  continue; // keep going while name same
12691  }
12692  if(AVEntry.LocationName != LocationNameToBeChecked)
12693  // if name different check forwards to see if repeats
12694  {
12695  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
12696  {
12697  if(TDEntry.ActionVector.at(a).Command == "cdt")
12698  {
12699  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
12700  }
12701  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
12702  {
12703  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
12705  // SecondPassMessage(GiveMessages, "Error in timetable - a location entry appears twice inappropriately for: " + TDEntry.HeadCode);
12706  // TrainDataVector.clear();
12707  // Utilities->CallLogPop(833);
12708  // return false;
12709  }
12710  }
12711  break; // out of the 'z' loop since have checked 'a' as far as need to
12712  }
12713  }
12714  y++;
12715  }
12716  }
12717 
12718  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
12719  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12720  {
12721  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12722  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12723  {
12724  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12725  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
12726  {
12727  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
12728  }
12729  AnsiString LocName = "";
12730  // dummy, only used so can call IsSNTEntryLocated
12731  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
12732  {
12733  if(AVEntry.LocationName == "")
12734  {
12735  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
12736  }
12737  }
12738  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
12739  {
12740  if(AVEntry.LocationName != "")
12741  {
12742  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
12743  }
12744  }
12745  }
12746  }
12747 
12748 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12749  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12750  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
12751 
12752  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
12753  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
12754 
12755  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
12756  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
12757  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
12758 */
12759  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
12760  {
12761  // non-shuttles & non-repeating links separately, but don't check that there isn't a
12762  // duplicate between a non-repeating shuttle and another - leave original tests in as
12763  // these also set the pointers
12764  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12765  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12766  {
12767  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12768  if(AVEntry.OtherHeadCode != "")
12769  {
12770  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
12771  {
12772  Utilities->CallLogPop(1584);
12773  return(false); // error message given in called function
12774  }
12775  }
12776  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12777  {
12778  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
12779  {
12780  Utilities->CallLogPop(1585);
12781  return(false); // error message given in called function
12782  }
12783  }
12784  }
12785  }
12786 
12787  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12788  {
12789  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12790  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12791  {
12792  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12793  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12794  {
12795  if(AVEntry.OtherHeadCode != "")
12796  {
12797  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12798  // false = non-shuttle
12799  {
12800  Utilities->CallLogPop(864);
12801  return(false); // error message given in called function
12802  }
12803  }
12804  }
12805  }
12806  }
12807 
12808  // now repeat the check just for the shuttles
12809  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12810  {
12811  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12812  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12813  {
12814  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12815  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12816  {
12817  if(AVEntry.OtherHeadCode != "")
12818  {
12819  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12820  // true = shuttle
12821  {
12822  Utilities->CallLogPop(1100);
12823  return(false); // error message given in called function
12824  }
12825  }
12826  }
12827  }
12828  }
12829 
12830  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12831  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12832  {
12833  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12834  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12835  {
12836  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12837  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12838  {
12840  {
12841  Utilities->CallLogPop(1060);
12842  return(false); // error message given in called function
12843  }
12844  }
12845  }
12846  }
12847 
12848  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12849  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12850  // don't ever need to and as designed would skip repeats
12851  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12852  {
12853  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12854  {
12855  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12856  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12857  {
12858  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12859  {
12860  Utilities->CallLogPop(1090);
12861  return(false); // error message given in called function
12862  }
12863  }
12864  }
12865  }
12866 
12867  // check all entries have all types set to something
12868  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12869  {
12870  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12871  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12872  {
12873  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12874  if(AVEntry.FormatType == NoFormat)
12875  {
12876  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12877  }
12878  else if(AVEntry.SequenceType == NoSequence)
12879  {
12880  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12881  }
12882  else if(AVEntry.LocationType == NoLocation)
12883  {
12884  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12885  }
12886  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12887  {
12888  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12889  }
12890  }
12891  }
12892 
12893  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12894  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12895  {
12896  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12897  // non-const reference so can alter content
12898  TTrainOperatingData TData;
12899  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12900  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12901  {
12902 /*
12903  class TTrainOperatingData
12904  {
12905  public:
12906  int TrainID; - default, set at construction
12907  TActionEventType EventReported; used during operation
12908  TRunningEntry RunningEntry; - default, set at construction
12909  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12910  };
12911 */
12912  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12913  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12914  {
12915  TDEntry.TrainOperatingDataVector.push_back(TData);
12916  }
12917  }
12918  else
12919  {
12920  TDEntry.NumberOfTrains = 1;
12921  }
12922  }
12923 
12924  // check that don't include any Continuation names
12925  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12926  {
12927  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12928  {
12929  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12930  AnsiString HC = TrainDataVector.at(x).HeadCode;
12931  if(LocName != "")
12932  {
12933  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12934  {
12935  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12936  TrainDataVector.clear();
12937  Utilities->CallLogPop(1578);
12938  return(false);
12939  }
12940  }
12941  }
12942  }
12943 
12944  // check that all repeat times below 96h
12945  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12946  {
12947  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12948  int IncMinutes = 0;
12949  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12950  {
12951  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12952  }
12953  else
12954  {
12955  continue; // basic times already checked in CheckTimeValidity
12956  }
12957  AnsiString HC = TrainDataVector.at(x).HeadCode;
12958  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12959  {
12960  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12961  {
12962  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12963  {
12964  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12965  TrainDataVector.clear();
12966  Utilities->CallLogPop(1818);
12967  return(false);
12968  }
12969  }
12970  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12971  {
12972  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12973  // 3d 23h 59m = 3.9993055556
12974  {
12975  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12976  TrainDataVector.clear();
12977  Utilities->CallLogPop(1819);
12978  return(false);
12979  }
12980  }
12981  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
12982  {
12983  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12984  // 3d 23h 59m = 3.9993055556
12985  {
12986  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12987  TrainDataVector.clear();
12988  Utilities->CallLogPop(1820);
12989  return(false);
12990  }
12991  }
12992  }
12993  }
12994 
12995  // Now that all set up change any extended headcodes back to ordinary headcodes
12996  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12997  {
12998  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
12999  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13000  {
13001  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13002  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13003  }
13004  }
13005 
13006  // SaveTrainDataVectorToFile(0);//test
13008  Utilities->CallLogPop(782);
13009  return(true);
13010 }
13011 
13012 // ---------------------------------------------------------------------------
13013 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13015 {
13016  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13017 }
13018 
13019 // ---------------------------------------------------------------------------
13020 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13022 {
13023  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13024  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13025  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13026 }
13027 
13028 // ---------------------------------------------------------------------------
13029 
13030 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13031 {
13032  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13033  if(HeadCode.Length() > 4) // ignore otherwise
13034  {
13035  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13036  }
13037  Utilities->CallLogPop(1593);
13038 }
13039 
13040 // ---------------------------------------------------------------------------
13041 
13042 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13043 {
13044  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13045  SecondHeadCode);
13046  int ForwardCount = 0;
13047  int ReverseCount = 0;
13048 
13049  if(MainHeadCode == SecondHeadCode)
13050  {
13051  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13052  TrainDataVector.clear();
13053  Utilities->CallLogPop(1594);
13054  return(false);
13055  }
13056  // forward check
13057  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13058  {
13059  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13060  if(TDEntry.HeadCode == MainHeadCode)
13061  {
13062  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13063  {
13064  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13065  if(AVEntry.OtherHeadCode == SecondHeadCode)
13066  {
13067  ForwardCount++;
13068  }
13069  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13070  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13071  {
13072  ForwardCount++;
13073  }
13074  }
13075  }
13076  }
13077  if(ForwardCount == 0)
13078  // this is an exception because the headcodes are selected in the same order as the forward check
13079  {
13080  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13081  }
13082  if(ForwardCount > 2)
13083  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13084  {
13085  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13086  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13087  TrainDataVector.clear();
13088  Utilities->CallLogPop(1587);
13089  return(false);
13090  }
13091  // reverse check
13092  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13093  {
13094  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13095  if(TDEntry.HeadCode == SecondHeadCode)
13096  {
13097  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13098  {
13099  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13100  if(AVEntry.OtherHeadCode == MainHeadCode)
13101  {
13102  ReverseCount++;
13103  }
13104  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13105  {
13106  ReverseCount++;
13107  }
13108  }
13109  }
13110  }
13111 
13112  if(ReverseCount == 0)
13113  {
13114  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13115  TrainDataVector.clear();
13116  Utilities->CallLogPop(1588);
13117  return(false);
13118  }
13119  if(ReverseCount > 2)
13120  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13121  {
13122  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13123  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13124  TrainDataVector.clear();
13125  Utilities->CallLogPop(1589);
13126  return(false);
13127  }
13128  if(ForwardCount != ReverseCount)
13129  {
13130  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13131  " than the other way round");
13132  TrainDataVector.clear();
13133  Utilities->CallLogPop(1610);
13134  return(false);
13135  }
13136  Utilities->CallLogPop(1590);
13137  return(true);
13138 }
13139 
13140 // ---------------------------------------------------------------------------
13141 
13142 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13143 /* Return false for no find or more than one find, check correct types of link
13144  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13145  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13146  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13147  Then do the same in reverse.
13148  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13149  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13150  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13151  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13152  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13153 
13154  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13155  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13156 
13157  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13158 */
13159 
13160 {
13161  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13162  int ForwardCount = 0;
13163  int ReverseCount = 0;
13164  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13165  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13166  TTrainDataEntry *MainTrainDataPtr = 0;
13167  TTrainDataEntry *OtherTrainDataPtr = 0;
13168 
13169  // forward check
13170  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13171  {
13172  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13173  if(TDEntry.HeadCode == MainHeadCode)
13174  {
13175  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13176  {
13177  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13178  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13179  {
13180  if(AVEntry.OtherHeadCode == OtherHeadCode)
13181  {
13182  MainTrainDataPtr = &TrainDataVector.at(x);
13183  ForwardEntryPtr = &AVEntry;
13184  ForwardCount++;
13185  ForwardTDVectorNumber = x;
13186  }
13187  }
13188  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13189  (AVEntry.Command == "Frh-sh")))
13190  {
13191  if(AVEntry.OtherHeadCode == OtherHeadCode)
13192  {
13193  MainTrainDataPtr = &TrainDataVector.at(x);
13194  ForwardEntryPtr = &AVEntry;
13195  ForwardCount++;
13196  ForwardTDVectorNumber = x;
13197  }
13198  }
13199  }
13200  }
13201  }
13202  if(ForwardCount == 0)
13203  // this is an exception because the headcodes are selected in the same order as the forward check
13204  {
13205  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13206  }
13207  if(ForwardCount > 1)
13208  {
13209  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13210  MainHeadCode);
13211  TrainDataVector.clear();
13212  Utilities->CallLogPop(836);
13213  return(false);
13214  }
13215  // reverse check
13216  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13217  {
13218  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13219  if(TDEntry.HeadCode == OtherHeadCode)
13220  {
13221  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13222  {
13223  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13224  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13225  {
13226  if(AVEntry.OtherHeadCode == MainHeadCode)
13227  {
13228  OtherTrainDataPtr = &TrainDataVector.at(x);
13229  ReverseCount++;
13230  ReverseEntryPtr = &AVEntry;
13231  ReverseTDVectorNumber = x;
13232  }
13233  }
13234  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13235  {
13236  if(AVEntry.OtherHeadCode == MainHeadCode)
13237  {
13238  OtherTrainDataPtr = &TrainDataVector.at(x);
13239  ReverseCount++;
13240  ReverseEntryPtr = &AVEntry;
13241  ReverseTDVectorNumber = x;
13242  }
13243  }
13244  }
13245  }
13246  }
13247 
13248  if(ReverseCount == 0)
13249  {
13250  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13251  TrainDataVector.clear();
13252  Utilities->CallLogPop(837);
13253  return(false);
13254  }
13255  if(ReverseCount > 1)
13256  {
13257  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13258  OtherHeadCode);
13259  TrainDataVector.clear();
13260  Utilities->CallLogPop(838);
13261  return(false);
13262  }
13263  // these will all be false for !Shuttle
13264  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13265  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13266  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13267  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13268 
13269  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13270  {
13271  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13272  TrainDataVector.clear();
13273  Utilities->CallLogPop(1058);
13274  return(false);
13275  }
13276  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13277  {
13278  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13279  TrainDataVector.clear();
13280  Utilities->CallLogPop(1059);
13281  return(false);
13282  }
13283  if(ForwardEntryPtr->LocationName == "")
13284  {
13285  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13286  ". One or other service does not have a location set");
13287  TrainDataVector.clear();
13288  Utilities->CallLogPop(526);
13289  return(false);
13290  }
13291  if(ReverseEntryPtr->LocationName == "")
13292  {
13293  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13294  ". One or other service does not have a location set");
13295  TrainDataVector.clear();
13296  Utilities->CallLogPop(527);
13297  return(false);
13298  }
13299  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13300  {
13301  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13302  " is at a different location to the referencing train " + MainHeadCode);
13303  TrainDataVector.clear();
13304  Utilities->CallLogPop(842);
13305  return(false);
13306  }
13307  // ignore shuttle repeat links for first time check
13308  if(!Shuttle)
13309  {
13310  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13311  {
13312  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13313  " has a different event time to the referencing train " + MainHeadCode);
13314  TrainDataVector.clear();
13315  Utilities->CallLogPop(525);
13316  return(false);
13317  }
13318  }
13319  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13320  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13321  if(ForwardShuttleStart && ReverseShuttleFinish)
13322  // Shuttle must be true if these are true
13323  {
13324  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13325  {
13326  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13327  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13328  TrainDataVector.clear();
13329  Utilities->CallLogPop(1055);
13330  return(false);
13331  }
13332  }
13333  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13334  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13335  {
13336  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13337  {
13338  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13339  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13340  TrainDataVector.clear();
13341  Utilities->CallLogPop(528);
13342  return(false);
13343  }
13344  }
13345  if(ReverseEntryPtr->Command == "Fjo")
13346  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13347  {
13348  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13349  {
13350  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13351  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13352  TrainDataVector.clear();
13353  Utilities->CallLogPop(862);
13354  return(false);
13355  }
13356  }
13357  if(ReverseEntryPtr->Command == "Fns")
13358  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13359  {
13360  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13361  {
13362  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13363  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13364  TrainDataVector.clear();
13365  Utilities->CallLogPop(529);
13366  return(false);
13367  }
13368  }
13369  if(ForwardEntryPtr->Command == "Sfs")
13370  {
13371  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13372  {
13373  SecondPassMessage(GiveMessages,
13374  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13375  MainHeadCode);
13376  TrainDataVector.clear();
13377  Utilities->CallLogPop(530);
13378  return(false);
13379  }
13380  }
13381  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13382  {
13383  if(ReverseEntryPtr->Command != "Sfs")
13384  {
13385  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13386  MainHeadCode);
13387  TrainDataVector.clear();
13388  Utilities->CallLogPop(839);
13389  return(false);
13390  }
13391  else
13392  {
13393  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13394  {
13395  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13396  TrainDataVector.clear();
13397  Utilities->CallLogPop(849);
13398  return(false);
13399  }
13400  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13401  {
13402  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13403  TrainDataVector.clear();
13404  Utilities->CallLogPop(850);
13405  return(false);
13406  }
13407  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13408  {
13409  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13410  TrainDataVector.clear();
13411  Utilities->CallLogPop(846);
13412  return(false);
13413  }
13414  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13415  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13416  if(OtherTrainDataPtr->Description == "")
13417  {
13418  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13419  }
13420  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13421  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13422  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13423  }
13424  }
13425  if(ForwardEntryPtr->Command == "Sns")
13426  {
13427  if(ReverseEntryPtr->Command != "Fns")
13428  {
13429  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13430  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13431  TrainDataVector.clear();
13432  Utilities->CallLogPop(531);
13433  return(false);
13434  }
13435  }
13436  if(ForwardEntryPtr->Command == "Fns")
13437  {
13438  if(ReverseEntryPtr->Command != "Sns")
13439  {
13440  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13441  " and forms a new service with headcode " + OtherHeadCode);
13442  TrainDataVector.clear();
13443  Utilities->CallLogPop(840);
13444  return(false);
13445  }
13446  else
13447  {
13448  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13449  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13450  if(OtherTrainDataPtr->Description == "")
13451  {
13452  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13453  }
13454  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13455  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13456  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13457  }
13458  }
13459  if(ForwardEntryPtr->Command == "jbo")
13460  {
13461  if(ReverseEntryPtr->Command != "Fjo")
13462  {
13463  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13464  " and is joined by a train with headcode " + OtherHeadCode);
13465  TrainDataVector.clear();
13466  Utilities->CallLogPop(841);
13467  return(false);
13468  }
13469  }
13470  if(ForwardEntryPtr->Command == "Fjo")
13471  {
13472  if(ReverseEntryPtr->Command != "jbo")
13473  {
13474  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13475  " and joins a train with headcode " + OtherHeadCode);
13476  TrainDataVector.clear();
13477  Utilities->CallLogPop(532);
13478  return(false);
13479  }
13480  else
13481  {
13482  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13483  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13484  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13485  {
13486  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13487  }
13488  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13489  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13490  }
13491  }
13492  if(ForwardShuttleStart)
13493  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13494  {
13495  if(!ReverseShuttleFinish)
13496  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13497  {
13498  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13499  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13500  TrainDataVector.clear();
13501  Utilities->CallLogPop(1056);
13502  return(false);
13503  }
13504  }
13505  if(ReverseShuttleStart)
13506  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13507  {
13508  if(!ForwardShuttleFinish)
13509  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
13510  {
13511  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
13512  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
13513  TrainDataVector.clear();
13514  Utilities->CallLogPop(1057);
13515  return(false);
13516  }
13517  else
13518  {
13519  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13520  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13521 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
13522  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
13523 */
13524  }
13525  }
13526  // check repeat information consistent if present
13527  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
13528  // and those not accessed here
13529 
13530  // still need to check the non-repeating links and that they have no repeats - do that outside this function
13531  bool MainRepeat = false, OtherRepeat = false;
13532  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
13533 
13534  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13535  {
13536  MainRepeat = true;
13537  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
13538  }
13539  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
13540  {
13541  OtherRepeat = true;
13542  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
13543  }
13544  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
13545  {
13546  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
13547  " and the associated train with headcode " + OtherHeadCode);
13548  TrainDataVector.clear();
13549  Utilities->CallLogPop(844);
13550  return(false);
13551  }
13552  if(MainRepeat && OtherRepeat)
13553  {
13554  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
13555  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
13556  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
13557  {
13558  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
13559  " and the associated train with headcode " + OtherHeadCode);
13560  TrainDataVector.clear();
13561  Utilities->CallLogPop(845);
13562  return(false);
13563  }
13564  }
13565  Utilities->CallLogPop(863);
13566  return(true);
13567 }
13568 
13569 // ---------------------------------------------------------------------------
13570 
13571 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
13572 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
13573 {
13574  // strip spaces from extreme ends of input
13575  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
13576  if(Input == "")
13577  {
13578  Utilities->CallLogPop(856);
13579  return;
13580  }
13581  while(Input[1] == ' ')
13582  {
13583  if(Input.Length() > 1)
13584  {
13585  Input = Input.SubString(2, Input.Length() - 1);
13586  }
13587  else
13588  {
13589  Input = "";
13590  Utilities->CallLogPop(857);
13591  return;
13592  }
13593  }
13594  if(Input == "")
13595  {
13596  Utilities->CallLogPop(858);
13597  return;
13598  }
13599  while(Input[Input.Length()] == ' ')
13600  {
13601  if(Input.Length() > 1)
13602  {
13603  Input = Input.SubString(1, Input.Length() - 1);
13604  }
13605  else
13606  {
13607  Input = "";
13608  Utilities->CallLogPop(859);
13609  return;
13610  }
13611  }
13612  // now strip spaces immediately after all commas and semicolons within the text
13613  AnsiString Output = "";
13614  bool DelimiterFound = false;
13615 
13616  for(int x = 1; x < Input.Length() + 1; x++)
13617  {
13618  if(DelimiterFound)
13619  {
13620  if(Input[x] == ' ')
13621  {
13622  continue;
13623  }
13624  }
13625  if((Input[x] != ',') && (Input[x] != ';'))
13626  {
13627  DelimiterFound = false;
13628  Output = Output + Input[x];
13629  }
13630  else
13631  {
13632  DelimiterFound = true;
13633  Output = Output + Input[x];
13634  }
13635  }
13636  if(Output == "")
13637  {
13638  Input = "";
13639  Utilities->CallLogPop(860);
13640  return;
13641  }
13642  // now strip spaces immediately before all commas and semicolons within the text
13643  Input = Output;
13644  Output = "";
13645  DelimiterFound = false;
13646  for(int x = Input.Length(); x > 0; x--)
13647  {
13648  if(DelimiterFound)
13649  {
13650  if(Input[x] == ' ')
13651  {
13652  continue;
13653  }
13654  }
13655  if((Input[x] != ',') && (Input[x] != ';'))
13656  {
13657  DelimiterFound = false;
13658  Output = AnsiString(Input[x]) + Output;
13659  }
13660  else
13661  {
13662  DelimiterFound = true;
13663  Output = AnsiString(Input[x]) + Output;
13664  }
13665  }
13666  Input = Output;
13667  Utilities->CallLogPop(861);
13668 }
13669 
13670 // ---------------------------------------------------------------------------
13671 
13672 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
13673 // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
13674 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
13675 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
13676 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
13677 // are done in this function, they must be done elsewhere.
13678 {
13679  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
13680  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13681 
13682  LocationName = "";
13683  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
13684  {
13685  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
13686  }
13688  {
13689  Utilities->CallLogPop(852);
13690  return(false);
13691  }
13692  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
13693  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
13694 
13695  if(LocRear != "")
13696  {
13697  LocationName = LocRear;
13698  }
13699  else
13700  {
13701  LocationName = LocFront;
13702  }
13703  if(LocationName == "")
13704  {
13705  Utilities->CallLogPop(1036);
13706  return(false);
13707  }
13708  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
13709  {
13710  Utilities->CallLogPop(1773);
13711  return(true);
13712  }
13713  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
13714  {
13715  LocationName = "";
13716  Utilities->CallLogPop(1784);
13717  return(false);
13718  }
13719  // here if not a signaller start entry so must be at least one more entry
13720  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
13721 
13722  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
13723  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
13724  {
13725  //added F-nshs at v2.5.1 so can stay at a
13726  Utilities->CallLogPop(1037); //location until become a new shuttle service
13727  return(true); //added Fns at same time as saw no reason to exclude
13728  }
13729  AnsiString TimeLocLocationName;
13730  bool FoundFlag = false;
13731 
13732  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
13733  {
13734  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
13735  if(AVEntry.FormatType == TimeLoc)
13736  {
13737  FoundFlag = true;
13738  TimeLocLocationName = AVEntry.LocationName;
13739  break;
13740  }
13741  }
13742  if(!FoundFlag)
13743  {
13744  Utilities->CallLogPop(853);
13745  return(false);
13746  }
13747  if(TimeLocLocationName == LocationName)
13748  {
13749  Utilities->CallLogPop(854);
13750  return(true);
13751  }
13752  Utilities->CallLogPop(855);
13753  return(false);
13754 }
13755 
13756 // ---------------------------------------------------------------------------
13757 
13758 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
13759 {
13760  // checks that the new train start elements are valid - both exist & are connected, and that not
13761  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
13762  // & not starting with front on a continuation
13763  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
13764  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
13765 
13766  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
13767  if(RearPosition < 0)
13768  // error message given in GetTrackVectorPositionFromString
13769  {
13770  Utilities->CallLogPop(759);
13771  return(false);
13772  }
13773  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
13774  if(FrontPosition < 0)
13775  // error message given in GetTrackVectorPositionFromString
13776  {
13777  Utilities->CallLogPop(760);
13778  return(false);
13779  }
13780  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
13781  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13782  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13783 
13784  // check front & rear connected
13785  for(int x = 0; x < 4; x++)
13786  {
13787  if(RearTrackElement.Conn[x] == FrontPosition)
13788  {
13789  RearExitPos = x;
13790  break;
13791  }
13792  if(x == 3)
13793  {
13794  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13795  Utilities->CallLogPop(762);
13796  return(false);
13797  }
13798  }
13799  // check not starting with front on a continuation
13800  if(FrontType == Continuation)
13801  {
13802  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13803  Utilities->CallLogPop(937);
13804  return(false);
13805  }
13806  // check not starting on a level crossing
13807  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13808  {
13809  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13810  Utilities->CallLogPop(1951);
13811  return(false);
13812  }
13813  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13814  {
13815  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13816  Utilities->CallLogPop(1952);
13817  return(false);
13818  }
13819  // check if trying to start on diverging leg of points
13820  if((RearType == Points) && (RearExitPos == 3))
13821  {
13822  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13823  Utilities->CallLogPop(936);
13824  return(false);
13825  }
13826  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13827  {
13828  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13829  Utilities->CallLogPop(1808);
13830  return(false);
13831  }
13832  Utilities->CallLogPop(905);
13833  return(true);
13834 }
13835 
13836 // ---------------------------------------------------------------------------
13837 
13838 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13839 // Rear & front element validity already checked in CheckStartPositionValidity
13840 // This checks for points in correct orientation, no train at start position and not starting on a locked route
13841 {
13842  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13843  AnsiString(RearExitPos));
13844  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13845 
13846  if(RearTrackElement.TrackType == Continuation)
13847  {
13848  EventType = FailTrainEntry;
13849  }
13850  else
13851  {
13852  EventType = FailCreateTrain;
13853  }
13854  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13855  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13856  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13857  TTrackType RearType = RearTrackElement.TrackType;
13858  TTrackType FrontType = FrontTrackElement.TrackType;
13859  AnsiString RearName, FrontName;
13860 
13861  if(RearTrackElement.ActiveTrackElementName != "")
13862  {
13863  RearName = RearTrackElement.ActiveTrackElementName;
13864  }
13865  else
13866  {
13867  RearName = RearTrackElement.ElementID;
13868  }
13869  if(FrontTrackElement.ActiveTrackElementName != "")
13870  {
13871  FrontName = FrontTrackElement.ActiveTrackElementName;
13872  }
13873  else
13874  {
13875  FrontName = FrontTrackElement.ElementID;
13876  }
13877  TPrefDirElement PrefDirElement; // needed for next function but not used
13878  int LockedVectorNumber; // needed for next function but not used
13879 
13880  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13881  {
13882  if(ReportFlag)
13883  {
13884  if(EventType == FailCreateTrain)
13885  {
13886  EventType = FailCreateLockedRoute;
13887  }
13888  else
13889  {
13890  EventType = FailEnterLockedRoute;
13891  }
13892  LogActionError(47, HeadCode, "", EventType, FrontName);
13893  }
13894  Utilities->CallLogPop(940);
13895  return(false);
13896  }
13897  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13898  {
13899  if(ReportFlag)
13900  {
13901  if(EventType == FailCreateTrain)
13902  {
13903  EventType = FailCreateLockedRoute;
13904  }
13905  else
13906  {
13907  EventType = FailEnterLockedRoute;
13908  }
13909  LogActionError(48, HeadCode, "", EventType, RearName);
13910  }
13911  Utilities->CallLogPop(1809);
13912  return(false);
13913  }
13914  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13915  {
13916  if(ReportFlag)
13917  {
13918  LogActionError(27, HeadCode, "", EventType, RearName);
13919  }
13920  Utilities->CallLogPop(1810);
13921  return(false);
13922  }
13923  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13924  {
13925  if(ReportFlag)
13926  {
13927  if(EventType == FailCreateTrain)
13928  {
13929  LogActionError(28, HeadCode, "", EventType, FrontName);
13930  }
13931  else
13932  {
13933  LogActionError(43, HeadCode, "", EventType, RearName);
13934  }
13935  }
13936  Utilities->CallLogPop(941);
13937  return(false);
13938  }
13939  if(RearType == Bridge)
13940  {
13941  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13942  {
13943  if(ReportFlag)
13944  {
13945  LogActionError(29, HeadCode, "", EventType, RearName);
13946  }
13947  Utilities->CallLogPop(942);
13948  return(false);
13949  }
13950  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13951  {
13952  if(ReportFlag)
13953  {
13954  LogActionError(30, HeadCode, "", EventType, RearName);
13955  }
13956  Utilities->CallLogPop(943);
13957  return(false);
13958  }
13959  }
13960  if(FrontType == Bridge)
13961  {
13962  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13963  {
13964  if(ReportFlag)
13965  {
13966  if(EventType == FailCreateTrain)
13967  {
13968  LogActionError(31, HeadCode, "", EventType, FrontName);
13969  }
13970  else
13971  {
13972  LogActionError(44, HeadCode, "", EventType, RearName);
13973  }
13974  }
13975  Utilities->CallLogPop(944);
13976  return(false);
13977  }
13978  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13979  {
13980  if(ReportFlag)
13981  {
13982  if(EventType == FailCreateTrain)
13983  {
13984  LogActionError(45, HeadCode, "", EventType, FrontName);
13985  }
13986  else
13987  {
13988  LogActionError(46, HeadCode, "", EventType, RearName);
13989  }
13990  }
13991  Utilities->CallLogPop(945);
13992  return(false);
13993  }
13994  }
13995  EventType = FailCreatePoints;
13996  if(RearType == Points)
13997  {
13998  if(RearTrackElement.Attribute == 1)
13999  {
14000  if(ReportFlag)
14001  {
14002  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14003  }
14004  Utilities->CallLogPop(933);
14005  return(false);
14006  }
14007  }
14008  if(FrontType == Points)
14009  {
14010  if(FrontTrackElement.Attribute == 1)
14011  {
14012  if(ReportFlag)
14013  {
14014  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14015  }
14016  Utilities->CallLogPop(934);
14017  return(false);
14018  }
14019  }
14020  Utilities->CallLogPop(939);
14021  return(true);
14022 }
14023 
14024 // ---------------------------------------------------------------------------
14025 
14026 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14027 {
14028  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14029  "," + AnsiString(IncDigits));
14030  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14031  {
14032  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14033  }
14034  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14035  {
14036  Utilities->CallLogPop(1893);
14037  return(BaseHeadCode);
14038  }
14039  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14040  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14041 
14042  while(NextRepeatDigits >= 100)
14043  {
14044  NextRepeatDigits -= 100; // rolls over after 99
14045  }
14046  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14047 
14048  if(NextRepeatDigitsStr.Length() < 2)
14049  {
14050  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14051  }
14052  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14053 
14054  Utilities->CallLogPop(1365);
14055  return(NextRepeatHeadCode);
14056 }
14057 
14058 // ---------------------------------------------------------------------------
14059 
14060 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14061 {
14062  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14063  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14064  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14065 
14066  Utilities->CallLogPop(1366);
14067  return(NextRepeatTime);
14068 }
14069 
14070 // ---------------------------------------------------------------------------
14071 
14072 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14073 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14074 {
14075  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14076  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14077  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14078  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14079  int RepeatSecs = RepeatMinutes * 60;
14080 
14081  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14082  {
14083  Utilities->CallLogPop(1367);
14084  return(false);
14085  }
14086  else
14087  {
14088  Utilities->CallLogPop(1368);
14089  return(true);
14090  }
14091 }
14092 
14093 // ---------------------------------------------------------------------------
14094 
14095 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14096 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14097 {
14098  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14099  NonRepeatingHeadCode);
14100  int ForwardCount = 0;
14101  int ReverseCount = 0;
14102  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14103  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14104  // Forward corresponds to Main, Reverse to Other
14105  TTrainDataEntry *MainTrainDataPtr = 0;
14106  TTrainDataEntry *OtherTrainDataPtr = 0;
14107 
14108  // forward check
14109  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14110  {
14111  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14112  if(TDEntry.HeadCode == MainHeadCode)
14113  {
14114  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14115  {
14116  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14117  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14118  {
14119  MainTrainDataPtr = &TrainDataVector.at(x);
14120  ForwardEntryPtr = &AVEntry;
14121  ForwardCount++;
14122  ForwardTDVectorNumber = x;
14123  }
14124  }
14125  }
14126  }
14127  if(ForwardCount == 0)
14128  // this is an exception because the headcodes are selected in the same order as the forward check
14129  {
14130  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14131  }
14132  if(ForwardCount > 1)
14133  {
14134  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14135  MainHeadCode);
14136  TrainDataVector.clear();
14137  Utilities->CallLogPop(1061);
14138  return(false);
14139  }
14140  // reverse check
14141  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14142  {
14143  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14144  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14145  {
14146  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14147  {
14148  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14149  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14150  {
14151  OtherTrainDataPtr = &TrainDataVector.at(x);
14152  ReverseCount++;
14153  ReverseEntryPtr = &AVEntry;
14154  ReverseTDVectorNumber = x;
14155  }
14156  }
14157  }
14158  }
14159 
14160  if(ReverseCount == 0)
14161  {
14162  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14163  TrainDataVector.clear();
14164  Utilities->CallLogPop(1062);
14165  return(false);
14166  }
14167  if(ReverseCount > 1)
14168  {
14169  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14170  NonRepeatingHeadCode);
14171  TrainDataVector.clear();
14172  Utilities->CallLogPop(1063);
14173  return(false);
14174  }
14175  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14176  {
14177  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14178  TrainDataVector.clear();
14179  Utilities->CallLogPop(1064);
14180  return(false);
14181  }
14182  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14183  {
14184  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14185  TrainDataVector.clear();
14186  Utilities->CallLogPop(1065);
14187  return(false);
14188  }
14189  if(ForwardEntryPtr->LocationName == "")
14190  {
14191  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14192  ". One or other service does not have a location set");
14193  TrainDataVector.clear();
14194  Utilities->CallLogPop(1066);
14195  return(false);
14196  }
14197  if(ReverseEntryPtr->LocationName == "")
14198  {
14199  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14200  ". One or other service does not have a location set");
14201  TrainDataVector.clear();
14202  Utilities->CallLogPop(1067);
14203  return(false);
14204  }
14205  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14206  {
14207  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14208  " is at a different location to the referencing train " + MainHeadCode);
14209  TrainDataVector.clear();
14210  Utilities->CallLogPop(1068);
14211  return(false);
14212  }
14213  if(ForwardEntryPtr->Command == "F-nshs")
14214  // i.e. the non repeating link into the shuttle service
14215  {
14216  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14217  {
14218  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14219  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14220  TrainDataVector.clear();
14221  Utilities->CallLogPop(1069);
14222  return(false);
14223  }
14224  }
14225  if(ForwardEntryPtr->Command == "Fns-sh")
14226  // i.e. the non repeating link out from the shuttle service
14227  {
14228  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14229  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14230  {
14231  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14232  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14233  TrainDataVector.clear();
14234  Utilities->CallLogPop(1070);
14235  return(false);
14236  }
14237  }
14238  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14239  // i.e. a non repeating link to or from the shuttle service
14240  {
14241  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14242  {
14243  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14244  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14245  TrainDataVector.clear();
14246  Utilities->CallLogPop(1071);
14247  return(false);
14248  }
14249  }
14250 /* it's allowed to have a different description
14251  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14252  {
14253  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14254  {
14255  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14256  TrainDataVector.clear();
14257  Utilities->CallLogPop(1072);
14258  return false;
14259  }
14260  }
14261 */
14262  if(ForwardEntryPtr->Command == "Sns-sh")
14263  {
14264  if(ReverseEntryPtr->Command != "F-nshs")
14265  {
14266  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14267  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14268  TrainDataVector.clear();
14269  Utilities->CallLogPop(1073);
14270  return(false);
14271  }
14272  }
14273  if(ForwardEntryPtr->Command == "F-nshs")
14274  {
14275  if(ReverseEntryPtr->Command != "Sns-sh")
14276  {
14277  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14278  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14279  TrainDataVector.clear();
14280  Utilities->CallLogPop(1074);
14281  return(false);
14282  }
14283  else
14284  {
14285  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14286  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14287  if(OtherTrainDataPtr->Description == "")
14288  {
14289  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14290  }
14291  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14292  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14293  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14294  }
14295  }
14296  if(ForwardEntryPtr->Command == "Sns-fsh")
14297  {
14298  if(ReverseEntryPtr->Command != "Fns-sh")
14299  {
14300  SecondPassMessage(GiveMessages,
14301  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14302  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14303  TrainDataVector.clear();
14304  Utilities->CallLogPop(1075);
14305  return(false);
14306  }
14307  }
14308  if(ForwardEntryPtr->Command == "Fns-sh")
14309  {
14310  if(ReverseEntryPtr->Command != "Sns-fsh")
14311  {
14312  SecondPassMessage(GiveMessages,
14313  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14314  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14315  TrainDataVector.clear();
14316  Utilities->CallLogPop(1076);
14317  return(false);
14318  }
14319  else
14320  {
14321  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14322  // links to the non-repeating non-shuttle linked service
14323  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14324  // needed for creating formatted timetable
14325  if(OtherTrainDataPtr->Description == "")
14326  {
14327  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14328  }
14329  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14330  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14331  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14332  }
14333  }
14334  Utilities->CallLogPop(1077);
14335  return(true);
14336 }
14337 
14338 // ---------------------------------------------------------------------------
14339 
14340 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14341 // Forward train is the finish shuttle entry 'Fns-sh'.
14342 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14343 {
14344  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14345  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14346  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14347  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14348  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14349 
14350  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14351  {
14352  Utilities->CallLogPop(1369);
14353  return(false);
14354  }
14355  else
14356  {
14357  Utilities->CallLogPop(1370);
14358  return(true);
14359  }
14360 }
14361 
14362 // ---------------------------------------------------------------------------
14363 
14364 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14365 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14366 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14367 // don't ever need to and as designed would skip repeats.
14368 
14369 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14370 {
14371  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14372  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14373  {
14374  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14375  }
14376  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14377  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14378  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14379 
14380  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14381  {
14382  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14383  TrainDataVector.clear();
14384  Utilities->CallLogPop(1091);
14385  return(false);
14386  }
14387  while(LastActionCommand == "Fns")
14388  {
14389  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14390  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14391  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14392  {
14393  SecondPassMessage(GiveMessages,
14394  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14395  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14396  TrainDataVector.clear();
14397  Utilities->CallLogPop(1092);
14398  return(false);
14399  }
14400  }
14401  // exit the 'while' with LastActionCommand FSH-XX
14402  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14403  {
14404  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14405  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14406  ". The linking of two or more shuttles is not permitted.");
14407  TrainDataVector.clear();
14408  Utilities->CallLogPop(1093);
14409  return(false);
14410  }
14411  Utilities->CallLogPop(1094);
14412  return(true);
14413 }
14414 
14415 // ---------------------------------------------------------------------------
14416 
14417 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14418 {
14419  if(!GiveMessages)
14420  {
14421  return;
14422  }
14423  // if(ServiceReference == "") ShowMessage(Message);
14424  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14425  {
14426  ShowMessage(Message);
14427  }
14428  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14429  // false means don't give messages within the function
14430  else
14431  {
14432  ShowMessage("Service " + ServiceReference + ": " + Message);
14433  }
14434 }
14435 
14436 // ---------------------------------------------------------------------------
14437 
14438 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14439 {
14440  if(!GiveMessages)
14441  {
14442  return;
14443  }
14444  ShowMessage(Message);
14445 }
14446 
14447 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14448 // ---------------------------------------------------------------------------
14449 
14450 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14451 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14452 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14453 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14454 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14455 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14456 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14457 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14458 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14459 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14460 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14461 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14462 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14463 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14464 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14465 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14466 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
14467 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
14468 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
14469 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
14470 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
14471 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
14472 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
14473 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
14474 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
14475 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
14476 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
14477 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
14478 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
14479 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
14480 {
14481  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
14482  AnsiString(ActionEventType) + "," + LocationID);
14483  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
14484 
14485  Prefix = " ERROR: ";
14486  if(ActionEventType == FailTrainEntry)
14487  {
14488  Prefix = " HELD: ";
14489  ErrorLog = " can't enter railway, train obstructing entry position ";
14490  WarningStr = " can't enter railway, train obstructing entry position ";
14491  Display->WarningLog(1, HeadCode + WarningStr + LocationID);
14492  }
14493  else if(ActionEventType == FailCreateTrain)
14494  {
14495  Prefix = " HELD: ";
14496  ErrorLog = " can't be created, train obstructing ";
14497  WarningStr = " can't be created, train obstructing ";
14498  Display->WarningLog(2, HeadCode + WarningStr + LocationID);
14499  }
14500  else if(ActionEventType == FailCreateLockedRoute)
14501  {
14502  Prefix = " HELD: ";
14503  ErrorLog = " can't be created on a locked route at ";
14504  WarningStr = " can't be created on a locked route at ";
14505  Display->WarningLog(4, HeadCode + WarningStr + LocationID);
14506  }
14507  else if(ActionEventType == FailEnterLockedRoute)
14508  {
14509  Prefix = " HELD: ";
14510  ErrorLog = " can't enter on a locked route at ";
14511  WarningStr = " can't enter on a locked route at ";
14512  Display->WarningLog(5, HeadCode + WarningStr + LocationID);
14513  }
14514  else if(ActionEventType == FailCreatePoints)
14515  {
14516  Prefix = " HELD: ";
14517  ErrorLog = " can't be created, diverging points at ";
14518  WarningStr = " can't be created, diverging points at ";
14519  Display->WarningLog(3, HeadCode + WarningStr + LocationID);
14520  }
14521  else if(ActionEventType == FailUnexpectedExitRailway)
14522  {
14523  ErrorLog = " left railway unexpectedly at ";
14524  UnexpectedExits++;
14525  }
14526  else if(ActionEventType == FailIncorrectExit)
14527  {
14528  ErrorLog = " left railway at an incorrect exit at ";
14529  IncorrectExits++;
14530  }
14531  else if(ActionEventType == FailLocTooShort)
14532  {
14533  ErrorLog = " failed to split - location too short at ";
14534  WarningStr = " failed to split, location too short at ";
14535  Display->WarningLog(6, HeadCode + WarningStr + LocationID);
14536  }
14537  else if(ActionEventType == FailSplitDueToOtherTrain)
14538  {
14539  Prefix = " HELD: ";
14540  ErrorLog = " unable to split - other train obstructing at ";
14541  WarningStr = " unable to split - other train obstructing at ";
14542  Display->WarningLog(7, HeadCode + WarningStr + LocationID);
14543  }
14544  else if(ActionEventType == FailUnexpectedBuffers)
14545  {
14546  ErrorLog = " stopped at buffers unexpectedly at position ";
14547  }
14548  else if(ActionEventType == FailMissedArrival)
14549  {
14550  ErrorLog = " failed to stop at ";
14551  MissedStops++;
14552  }
14553  else if(ActionEventType == FailMissedSplit)
14554  {
14555  ErrorLog = " failed to split at ";
14557  }
14558  else if(ActionEventType == FailMissedJBO)
14559  {
14560  ErrorLog = " failed to be joined by other train at ";
14562  }
14563  else if(ActionEventType == FailMissedJoinOther)
14564  {
14565  ErrorLog = " failed to join other train at ";
14567  }
14568  else if(ActionEventType == FailMissedTerminate)
14569  {
14570  ErrorLog = " failed to terminate at ";
14572  }
14573  else if(ActionEventType == FailMissedNewService)
14574  {
14575  ErrorLog = " failed to form new service at ";
14577  }
14578  else if(ActionEventType == FailMissedExitRailway)
14579  {
14580  ErrorLog = " failed to exit railway ";
14582  }
14583  else if(ActionEventType == FailMissedChangeDirection)
14584  {
14585  ErrorLog = " failed to change direction at ";
14587  }
14588  else if(ActionEventType == FailMissedPass)
14589  {
14590  ErrorLog = " failed to pass ";
14592  }
14593  else if(ActionEventType == FailBuffersPreventingStart)
14594  {
14595  ErrorLog = " facing buffers and unable to start at ";
14596  }
14597  else if(ActionEventType == FailDerailed)
14598  {
14599  ErrorLog = " DERAILED at position ";
14600  Prefix = " DERAILMENT: ";
14601  Derailments++;
14602  }
14603  else if(ActionEventType == FailBufferCrash)
14604  {
14605  ErrorLog = " CRASHED INTO BUFFERS at ";
14606  Prefix = " CRASH: ";
14607  CrashedTrains++;
14608  }
14609  else if(ActionEventType == FailLevelCrossingCrash)
14610  {
14611  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
14612  Prefix = " CRASH: ";
14613  CrashedTrains++;
14614  }
14615  else if(ActionEventType == FailCrashed)
14616  {
14617  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
14618  Prefix = " CRASH: ";
14619  CrashedTrains++;
14620  CrashedTrains++;
14621  }
14622  else if(ActionEventType == FailSPAD)
14623  {
14624  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
14625  Prefix = " SPAD: ";
14626  SPADEvents++;
14627  }
14628  else if(ActionEventType == FailLockedRoute)
14629  {
14630  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
14631  Prefix = " SPAD RISK: ";
14632  SPADRisks++;
14633  }
14634  else if(ActionEventType == RouteForceCancelled)
14635  {
14636  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
14637  }
14638  else if(ActionEventType == WaitingForJBO)
14639  {
14640  Prefix = " WARNING: ";
14641  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
14642  WarningStr = " waiting to join " + OtherHeadCode + " at ";
14643  Display->WarningLog(8, HeadCode + WarningStr + LocationID);
14644  }
14645  else if(ActionEventType == WaitingForFJO)
14646  {
14647  Prefix = " WARNING: ";
14648  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
14649  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
14650  Display->WarningLog(9, HeadCode + WarningStr + LocationID);
14651  }
14652  TDateTime ActualTime = TrainController->TTClockTime;
14653 
14654  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
14655  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
14656  Utilities->CallLogPop(1371);
14657 }
14658 
14659 // ---------------------------------------------------------------------------
14660 
14662 {
14663 /*
14664  TrainDataEntry
14665  AnsiString HeadCode, Description;//null on creation
14666  int StartSpeed, MaxRunningSpeed;//both kph
14667  int RepeatNumber;
14668  TActionVector ActionVector;
14669  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
14670  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
14671 
14672  ActionVectorEntry
14673  TTimetableEntryType FormatType;
14674  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
14675  AnsiString LocationName, Command, OtherHeadCode;//null on creation
14676  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
14677  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14678  int RepeatNumber;
14679 
14680  TrainOperatingData
14681  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
14682  int TrainID;
14683  TRunningEntry RunningEntry;
14684  TDateTime StartTime;
14685  AnsiString HeadCode;
14686 */
14687  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
14688  std::ofstream OutFile("TrainData.csv");
14689 
14690  if(OutFile == 0)
14691  {
14692  ShowMessage("Output file TrainData.csv failed to open");
14693  Utilities->CallLogPop(1372);
14694  return;
14695  }
14696  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14697  {
14698  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14699  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
14700 
14701  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
14702  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
14703 
14704  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
14705  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
14706  "RepeatNumber" << '\n' << '\n';
14707  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14708  {
14709  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14710  AnsiString TimetableEntryTypeStr;
14711  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
14712  switch(AVEntry.FormatType)
14713  {
14714  case 0:
14715  {
14716  TimetableEntryTypeStr = "NoFormat";
14717  break;
14718  }
14719 
14720  case 1:
14721  {
14722  TimetableEntryTypeStr = "TimeLoc";
14723  break;
14724  }
14725 
14726  case 2:
14727  {
14728  TimetableEntryTypeStr = "TimeTimeLoc";
14729  break;
14730  }
14731 
14732  case 3:
14733  {
14734  TimetableEntryTypeStr = "TimeCmd";
14735  break;
14736  }
14737 
14738  case 4:
14739  {
14740  TimetableEntryTypeStr = "StartNew";
14741  break;
14742  }
14743 
14744  case 5:
14745  {
14746  TimetableEntryTypeStr = "TimeCmdHeadCode";
14747  break;
14748  }
14749 
14750  case 6:
14751  {
14752  TimetableEntryTypeStr = "FinRemHere";
14753  break;
14754  }
14755 
14756  case 7:
14757  {
14758  TimetableEntryTypeStr = "FNSShuttle";
14759  break;
14760  }
14761 
14762  case 8:
14763  {
14764  TimetableEntryTypeStr = "SNTShuttle";
14765  break;
14766  }
14767 
14768  case 9:
14769  {
14770  TimetableEntryTypeStr = "SNSShuttle";
14771  break;
14772  }
14773 
14774  case 10:
14775  {
14776  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
14777  break;
14778  }
14779 
14780  case 11:
14781  {
14782  TimetableEntryTypeStr = "FSHNewService";
14783  break;
14784  }
14785 
14786  case 12:
14787  {
14788  TimetableEntryTypeStr = "Repeat";
14789  break;
14790  }
14791 
14792  default:
14793  {
14794  TimetableEntryTypeStr = "Default";
14795  break;
14796  }
14797  }
14798  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
14799  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
14800  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
14801  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
14802  AVEntry.NumberOfRepeats << '\n';
14803  }
14804  OutFile << '\n';
14805  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
14806  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
14807  {
14808  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
14809  AnsiString RunningEntryStr;
14810  // NotStarted, Running, Exited
14811  switch(TOD.RunningEntry)
14812  {
14813  case 0:
14814  {
14815  RunningEntryStr = "NotStarted";
14816  break;
14817  }
14818 
14819  case 1:
14820  {
14821  RunningEntryStr = "Running";
14822  break;
14823  }
14824 
14825  case 2:
14826  {
14827  RunningEntryStr = "Exited";
14828  break;
14829  }
14830  }
14831  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14832  }
14833  OutFile << '\n';
14834  }
14835  OutFile.close();
14836  Utilities->CallLogPop(1373);
14837 }
14838 
14839 // ---------------------------------------------------------------------------
14840 
14841 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14842 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14843 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14844 {
14845  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14846  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14848  ShowMessage(Message);
14849  BaseTime = TDateTime::CurrentDateTime();
14850  StopTTClockFlag = false;
14851  Utilities->CallLogPop(1374);
14852 }
14853 
14854 // ---------------------------------------------------------------------------
14855 
14856 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14857 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14858 // from the start of the relevant vectors. Can't save the pointer values
14859 // as these will be different each time the vectors are created
14860 {
14861  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14862  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14863  for(unsigned int x = 0; x < TrainVector.size(); x++)
14864  {
14865  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14866  }
14867  Utilities->CallLogPop(1375);
14868 }
14869 
14870 // ---------------------------------------------------------------------------
14871 
14872 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14873 {
14874  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14875  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14876  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14877  // by zero error in calculating AValue, use 1
14878  for(int x = 0; x < NumberOfTrains; x++)
14879  {
14880  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14881  // by zero error in calculating AValue, use 1
14882  NewTrain->LoadOneSessionTrain(0, SessionFile);
14883  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14884  // added at v2.4.0. have to include as that value not stored in session file
14885  {
14886  NewTrain->StoppedWithoutPower = true;
14887  }
14888  TrainVector.push_back(*NewTrain);
14889  LastTrainLoaded = x;
14890  }
14891  delete NewTrain;
14892  Utilities->CallLogPop(1376);
14893 }
14894 
14895 // ---------------------------------------------------------------------------
14896 
14897 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14898 {
14899  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14900  int NumberOfTrains;
14901 
14902  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14903  {
14904  Utilities->CallLogPop(1377);
14905  return(false);
14906  }
14907  for(int x = 0; x < NumberOfTrains; x++)
14908  {
14909  if(!(TTrain::CheckOneSessionTrain(InFile)))
14910  {
14911  Utilities->CallLogPop(1378);
14912  return(false);
14913  }
14914  }
14915  Utilities->CallLogPop(1379);
14916  return(true);
14917 }
14918 
14919 // ---------------------------------------------------------------------------
14920 
14921 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14922 {
14923  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14924  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14925  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14926  {
14927  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14928  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14929  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14930  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14931  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14932  }
14933  Utilities->CallLogPop(1380);
14934 }
14935 
14936 // ---------------------------------------------------------------------------
14937 
14938 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14939 {
14940  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14941  TAllRoutes::TLockedRouteClass LockedRouteObject;
14942  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14943 
14944  for(int x = 0; x < LockedRouteVectorSize; x++)
14945  {
14946  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14947  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14948  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14949  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
14950  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
14951  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
14952  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
14953  }
14954  Utilities->CallLogPop(1381);
14955 }
14956 
14957 // ---------------------------------------------------------------------------
14958 
14959 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14960 {
14961  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
14962  int LockedRouteVectorSize;
14963 
14964  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
14965  {
14966  Utilities->CallLogPop(1382);
14967  return(false);
14968  }
14969  for(int x = 0; x < LockedRouteVectorSize; x++)
14970  {
14971  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14972  {
14973  Utilities->CallLogPop(1383);
14974  return(false);
14975  }
14976  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14977  {
14978  Utilities->CallLogPop(1384);
14979  return(false);
14980  }
14981  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14982  {
14983  Utilities->CallLogPop(1385);
14984  return(false);
14985  }
14986  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14987  {
14988  Utilities->CallLogPop(1386);
14989  return(false);
14990  }
14991  if(!Utilities->CheckFileDouble(SessionFile))
14992  {
14993  Utilities->CallLogPop(1387);
14994  return(false);
14995  }
14996  }
14997  Utilities->CallLogPop(1388);
14998  return(true);
14999 }
15000 
15001 // ---------------------------------------------------------------------------
15002 
15003 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15004 {
15005  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15006  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15007  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15008  {
15009  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15010  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15011  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15012  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15013  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15014  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15015  }
15016  Utilities->CallLogPop(1389);
15017 }
15018 
15019 // ---------------------------------------------------------------------------
15020 
15021 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15022 {
15023  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15024  TContinuationAutoSigEntry ContinuationAutoSigObject;
15025  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15026 
15027  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15028  {
15029  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15030  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15031  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15032  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15033  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15034  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15035  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15036  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15037  }
15038  Utilities->CallLogPop(1390);
15039 }
15040 
15041 // ---------------------------------------------------------------------------
15042 
15043 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15044 {
15045  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15046  int ContinuationAutoSigVectorSize;
15047 
15048  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15049  {
15050  Utilities->CallLogPop(1391);
15051  return(false);
15052  }
15053  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15054  {
15055  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15056  {
15057  Utilities->CallLogPop(1392);
15058  return(false);
15059  }
15060  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15061  {
15062  Utilities->CallLogPop(1393);
15063  return(false);
15064  }
15065  if(!Utilities->CheckFileDouble(SessionFile))
15066  {
15067  Utilities->CallLogPop(1405);
15068  return(false);
15069  }
15070  if(!Utilities->CheckFileDouble(SessionFile))
15071  {
15072  Utilities->CallLogPop(1406);
15073  return(false);
15074  }
15075  if(!Utilities->CheckFileDouble(SessionFile))
15076  {
15077  Utilities->CallLogPop(1407);
15078  return(false);
15079  }
15080  if(!Utilities->CheckFileDouble(SessionFile))
15081  {
15082  Utilities->CallLogPop(1394);
15083  return(false);
15084  }
15085  }
15086  Utilities->CallLogPop(1395);
15087  return(true);
15088 }
15089 
15090 // ---------------------------------------------------------------------------
15091 
15092 /*
15093  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15094  {
15095  public:
15096  AnsiString Description; ///< service description
15097  AnsiString HeadCode; ///< service headcode
15098  int RepeatNumber; ///< service RepeatNumber
15099  int IncrementalMinutes; ///< Repeat separation in minutes
15100  int IncrementalDigits; ///< Repeat headcode separation
15101  int VectorPosition; ///< TrackVectorPosition for the continuation element
15102  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15103  };
15104 
15105 
15106  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15107  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15108 */
15109 
15111 // build this into timetable load so session loading can use it too
15112 // being a multimap it automatically sorts in ascending EventTime order
15113 {
15114  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15116  // need to clear as this called twice when load a session
15117  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15118  {
15119  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15120  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15121  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15122 
15123  if(AVFirstEntry.Command == "Snt")
15124  // new train (no need to include Snt-sh since they can't start at a continuation)
15125  {
15128  {
15130  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15131  // retains this value for all repeats
15132  CTEEntry.RepeatNumber = 0; // for first entry
15133  CTEEntry.TrainDataEntryPtr = &TDEntry;
15134  // retains this value for all repeats
15135  CTEEntry.HeadCode = TDEntry.HeadCode;
15136  CTEEntry.Description = TDEntry.Description;
15137  CTEEntry.IncrementalMinutes = 0;
15138  CTEEntry.IncrementalDigits = 0;
15139  if(AVLastEntry.FormatType == Repeat)
15140  {
15141  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15142  // retains this value or 0 for all repeats
15143  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15144  // retains this value or 0 for all repeats
15145  }
15146  CTEMMP.first = AVFirstEntry.EventTime;
15147  CTEMMP.second = CTEEntry;
15148  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15149  // base entry
15150  if(TDEntry.NumberOfTrains > 1)
15151  {
15152  if(AVLastEntry.FormatType != Repeat)
15153  {
15154  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15155  }
15156  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15157  {
15158  CTEEntry.RepeatNumber = y;
15159  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15160  // CTEEntry.VectorPosition stays same
15161  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15162  CTEMMP.second = CTEEntry;
15163  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15164  }
15165  }
15166  }
15167  }
15168  }
15169  Utilities->CallLogPop(1396);
15170 }
15171 
15172 // ---------------------------------------------------------------------------
15173 
15175 {
15176  // called when WarningFlashCount == 0 or when press zoomout button
15177  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15178  if(!Display->ZoomOutFlag)
15179  {
15180  Utilities->CallLogPop(1156);
15181  return;
15182  }
15183  for(unsigned int x = 0; x < TrainVector.size(); x++)
15184  {
15185  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15186  // if OldPlotElement[x] == -1 then ignore (not plotted)
15188  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15189  }
15190  Display->Update();
15191  // need to keep this since Update() not called for PlotSmallOutput as too slow
15192  Utilities->CallLogPop(742);
15193 }
15194 
15195 // ---------------------------------------------------------------------------
15196 
15197 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15198 {
15199  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15200  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15201  {
15202  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15203  }
15204  Utilities->CallLogPop(740);
15205  return(TrainVector.at(VecPos));
15206 }
15207 
15208 // ---------------------------------------------------------------------------
15209 
15210 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15211 {
15212  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15213  AnsiString RetStr = "", PartStr = "";
15214 
15215 
15216 /*
15217  Have description & mass etc for train at top - header, then array of actions
15218 
15219  class TActionVectorEntry
15220  {
15221  public:
15222  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15224  bool SignallerControl;
15226  bool Warning;
15228  int NumberOfRepeats;
15230  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15232  TDateTime EventTime, ArrivalTime, DepartureTime;
15234  TExitList ExitList;
15236  TTimetableFormatType FormatType;
15238  TTimetableLocationType LocationType;
15240  TTimetableSequenceType SequenceType;
15242  TTimetableShuttleLinkType ShuttleLinkType;
15244  TTrainDataEntry *LinkedTrainEntryPtr;
15246  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15248 
15249  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15250 
15251  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15252 
15253  class TTrainOperatingData
15254  {
15255  public:
15256  int TrainID;
15257  TActionEventType EventReported;
15258  TRunningEntry RunningEntry;
15259 
15260  //inline function
15261  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15262  };
15263 
15264  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15265 
15266  class TTrainDataEntry
15267  {
15268  public:
15269  AnsiString HeadCode, ServiceReference, Description;
15271  double MaxBrakeRate;
15273  double MaxRunningSpeed;
15275  double PowerAtRail;
15277  int Mass;
15279  int NumberOfTrains;
15281  int SignallerSpeed;
15283  int StartSpeed;
15285  TActionVector ActionVector;
15287  TTrainOperatingDataVector TrainOperatingDataVector;
15289 
15290  //inline function
15291  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15292  };
15293 
15294  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15295 
15296  //formatted timetable types
15297  class TOneTrainFormattedEntry
15298  {
15299  AnsiString Action;//includes location if relevanr
15300  AnsiString Time;
15301  };
15302 
15303  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15304 
15305  class TOneCompleteFormattedTrain//headcode + list of actions
15306  {
15307  public:
15308  AnsiString HeadCode;
15309  TOneFormattedTrainVector OneFormattedTrainVector;
15310  };
15311 
15312  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15313 
15314  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15315  {
15316  public:
15317  AnsiString Header;//description, mass, power, brake rate etc
15318  int NumberOfTrains;// number of repeats + 1
15319  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15320  };
15321 
15322 
15323  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15324  //end of formatted timetable types
15325 
15326 */
15327 
15328  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15329 
15330  // format "16/06/2009 20:55:17"
15331  // avoid characters in filename:= / \ : * ? " < > |
15332  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15333 
15334  AnsiString ShortTTName = "";
15335 
15336  for(int x = TTFileName.Length(); x > 0; x--)
15337  {
15338  if(TTFileName[x] == '\\')
15339  {
15340  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15341  break;
15342  }
15343  }
15344 
15345  ShowMessage("Creates two timetables named " + ShortTTName +
15346  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15347 
15348  Screen->Cursor = TCursor(-11); // Hourglass
15349 
15350  AnsiString FormatNoDPStr = "#######0";
15351  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15352 
15354  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15355  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15356 
15357  // all timetable in formatted form
15358  //create the AllTTTrains vector
15359  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15360  {
15361  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15362  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15363  if(TrainDataEntry.Mass > 0)
15364  {
15365  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15366  }
15367  if(TrainDataEntry.PowerAtRail > 0)
15368  {
15369  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15370  }
15371  if(TrainDataEntry.MaxBrakeRate > 0)
15372  {
15373  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15374  }
15375  if(TrainDataEntry.MaxRunningSpeed > 0)
15376  {
15377  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15378  }
15379  FirstHeadCode = TrainDataEntry.HeadCode;
15380  int IncDigits = 0, IncMinutes = 0;
15381  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15382  if(!ActionVector.empty())
15383  {
15384  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15385  {
15386  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15387  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15388  }
15389  }
15390  TTrainFormattedInformation OneTTLine;
15391  // contains all information for a single TT entry (including repeats)
15392  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15393  {
15394  OneTTLine.Header = "";
15395  if((TrainDataEntry.Description != "") && (MassStr != ""))
15396  {
15397  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15398  }
15399  else if(TrainDataEntry.Description != "")
15400  {
15401  OneTTLine.Header = TrainDataEntry.Description;
15402  }
15403  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15404  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15405  for(unsigned int z = 0; z < ActionVector.size(); z++)
15406  {
15407  TOneTrainFormattedEntry OneTTEntry;
15408  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15409  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15410  AnsiString PartStr = "", TimeStr = "";
15411 /*
15412  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15413  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15414  ExitRailway};
15415  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15416  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15417  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15418 */
15419  if(ActionVectorEntry.SequenceType == Start)
15420  {
15421  if(ActionVectorEntry.FormatType == StartNew)
15422  {
15423  if(ActionVectorEntry.LocationName != "")
15424  {
15425  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15426  {
15427  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15428  }
15429  else
15430  {
15431  PartStr = "Created at " + ActionVectorEntry.LocationName;
15432  }
15433  }
15434  else // may be a named continuation or other element, and if so report that
15435  {
15436  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15437  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15438  {
15439  if(LocName != "")
15440  {
15441  PartStr = "Enters at " + LocName;
15442  }
15443  else // use rear position if it's a continuation
15444  {
15445  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15446  }
15447  }
15448  else // not a continuation
15449  {
15450  if(LocName != "")
15451  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15452  // but include anyway
15453  {
15454  PartStr = "Created at " + LocName;
15455  }
15456  else // use rear position again
15457  {
15458  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15459  }
15460  }
15461  }
15462  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
15463  }
15464  else if(ActionVectorEntry.FormatType == SNTShuttle)
15465  {
15466  if(y == 0) // first train
15467  {
15468  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15469  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
15470  }
15471  else
15472  {
15473  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15474  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15475  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
15476  } // y-1 for headcode above since it is the last repeat value that the train is from
15477 
15478  }
15479  else if(ActionVectorEntry.Command == "Sfs")
15480  {
15481  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
15482  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15483  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
15484  }
15485  else if(ActionVectorEntry.Command == "Sns")
15486  {
15487  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15488  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15489  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
15490  }
15491  else if(ActionVectorEntry.FormatType == SNSShuttle)
15492  {
15493  if(y == 0) // first entry from shuttle
15494  {
15495  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15496  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
15497  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
15498  }
15499  else
15500  {
15501  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
15502  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
15503  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
15504  } // y-1 for headcode above since it is the last repeat value that the train is from
15505 
15506  }
15507  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
15508  {
15509  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
15510  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
15511  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
15512  AnsiString FirstHeadCode = TDE->HeadCode;
15513  int LastRepeatNumber = TDE->NumberOfTrains - 1;
15514  // a shuttle has to have at least 1 repeat
15515  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
15516  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
15517  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
15518  }
15519  }
15520  else if(ActionVectorEntry.SequenceType == Intermediate)
15521  {
15522  if(ActionVectorEntry.FormatType == TimeTimeLoc)
15523  {
15524  // here need 2 entries if times different so push the first right away & the second later
15525  // if times same just give the arrival entry
15526  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
15527  {
15528  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15529  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15530  OneTTEntry.Action = PartStr;
15531  OneTTEntry.Time = TimeStr;
15532  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15533  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15534  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
15535  }
15536  else
15537  {
15538  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
15539  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15540  }
15541  }
15542  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
15543  {
15544  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
15545  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
15546  }
15547  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
15548  {
15549  PartStr = "Departs from " + ActionVectorEntry.LocationName;
15550  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
15551  }
15552  else if(ActionVectorEntry.FormatType == PassTime)
15553  {
15554  PartStr = "Passes " + ActionVectorEntry.LocationName;
15555  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
15556  }
15557  else if(ActionVectorEntry.Command == "jbo")
15558  {
15559  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
15560  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15561  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
15562  }
15563  else if(ActionVectorEntry.Command == "fsp")
15564  {
15565  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
15566  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15567  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
15568  }
15569  else if(ActionVectorEntry.Command == "rsp")
15570  {
15571  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
15572  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15573  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
15574  }
15575  else if(ActionVectorEntry.Command == "cdt")
15576  {
15577  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
15578  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
15579  }
15580  }
15581  else if(ActionVectorEntry.SequenceType == Finish)
15582  {
15583  if(ActionVectorEntry.Command == "Fns")
15584  {
15585  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15586  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15587  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
15588  }
15589  else if(ActionVectorEntry.Command == "F-nshs")
15590  {
15591  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15592  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15593  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
15594  }
15595  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15596  {
15597  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
15598  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15599  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
15600  // y+1 because it's the NEXT service repeat number that is relevant
15601  }
15602  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15603  {
15604  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15605  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
15606  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
15607  }
15608  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
15609  {
15610  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
15611  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
15612  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
15613  // y+1 because it's the NEXT service repeat number that is relevant
15614  }
15615  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
15616  {
15617  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
15618  // only used in chronological tt
15619  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
15620  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
15621  }
15622  else if(ActionVectorEntry.Command == "Frh")
15623  {
15624  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
15625  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
15626  if(z > 0)
15627  // should be for finish entry but include check for safety
15628  {
15629  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15630  {
15631  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15632  }
15633  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15634  {
15635  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15636  }
15637  else
15638  {
15639  TimeStr = " "; // shouldn't ever get here
15640  }
15641  }
15642  }
15643  else if(ActionVectorEntry.Command == "Fer")
15644  {
15645  AnsiString AllowedExits;
15646  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
15647  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
15648  }
15649  else if(ActionVectorEntry.Command == "Fjo")
15650  {
15651  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
15652  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
15653  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
15654  }
15655  }
15656  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
15657  {
15658  continue; // no entry needed for a repeat
15659  }
15660  OneTTEntry.Action = PartStr;
15661  OneTTEntry.Time = TimeStr;
15662  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
15663  // one per action
15664  }
15665  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
15666  // one per repeat
15667  }
15668  AllTTTrains->push_back(OneTTLine); // one per repeating train
15669  }
15670  // AllTTTrains vector now complete
15671 
15672  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
15673 
15674  if(TTFile == 0)
15675  {
15676  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
15677  delete AllTTTrains;
15678  Utilities->CallLogPop(1567);
15679  return;
15680  }
15681 /* formatted timetable types
15682  class TOneTrainFormattedEntry
15683  {
15684  AnsiString Action;//includes location if relevant
15685  AnsiString Time;
15686  };
15687 
15688  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15689 
15690  class TOneCompleteFormattedTrain//headcode + list of actions
15691  {
15692  public:
15693  AnsiString HeadCode;
15694  TOneFormattedTrainVector OneFormattedTrainVector;
15695  };
15696 
15697  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15698 
15699  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15700  {
15701  public:
15702  AnsiString Header;//description, mass, power, brake rate etc
15703  int NumberOfTrains;// number of repeats + 1
15704  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15705  };
15706 
15707  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15708  //end of formatted timetable types
15709 */
15710 
15711  // new layout using multiple rows
15712  TTFile << TableTitle.c_str() << '\n' << '\n';
15713  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15714  {
15715  TTFile << AllTTTrains->at(x).Header.c_str();
15716  TTFile << '\n';
15717  TTFile << ','; // for the blank line above the action list
15718  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15719  {
15720  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15721  {
15722  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
15723  }
15724  else
15725  {
15726  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
15727  }
15728  }
15729  TTFile << '\n' << '\n';
15730 
15731  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
15732  {
15733  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
15734  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15735  {
15736  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
15737  {
15738  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
15739  }
15740  else
15741  {
15742  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
15743  }
15744  }
15745  TTFile << '\n';
15746  }
15747  TTFile << '\n' << '\n';
15748  }
15749 
15750  TTFile.close();
15751 
15752  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15753 
15754  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15755 
15756  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
15757 
15758  if(TTFile2 == 0)
15759  {
15760  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
15761  delete AllTTTrains;
15762  Utilities->CallLogPop(1710);
15763  return;
15764  }
15765  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
15766  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
15767  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
15768 
15769  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
15770  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
15771 
15772  // multimap of AnsiStrings with TimeString as key (to sort automatically)
15773 
15774  TTFile2 << TableTitle.c_str() << '\n' << '\n';
15775  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
15776  {
15777  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
15778  {
15779  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
15780  {
15781  bool GiveMessagesFalse = false;
15782  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
15783  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
15784  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
15785  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
15786  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
15787  {
15788  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
15789  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
15790  TimeString = TimeString.SubString(9, 5);
15791  ActionString += " " + OtherHeadCode;
15792  }
15793  if(TimeString.SubString(1, 7) == "End at ")
15794  // for Frh-sh final entry
15795  {
15796  TimeString = TimeString.SubString(8, 5);
15797  }
15798  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
15799  AnsiMultiMapEntry.first = TimeString;
15800  AnsiMultiMapEntry.second = OneLine;
15801  TAMM->insert(AnsiMultiMapEntry);
15802  }
15803  }
15804  }
15805 
15806  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
15807  {
15808  TTFile2 << (AMMIT->second).c_str();
15809  }
15810  delete AllTTTrains;
15811  delete TAMM;
15812  TTFile2.close();
15813  Utilities->CallLogPop(1580);
15814 }
15815 
15816 // ---------------------------------------------------------------------------
15817 
15818 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
15819  bool AtLocChecked, int ArrRange, int DepRange)
15820 {
15821 
15822  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
15823  bool AnalysisError = false;
15824 
15825  try
15826  {
15827  //New section after v2.4.3 for tt conflict analysis
15828  /*
15829  typedef std::list<AnsiString> TServiceCallingLocsList;
15830  typedef std::vector<TServiceCallingLocsList> TAllServiceCallingLocsMap;
15831 
15832  struct TLocServiceTimes
15833  {
15834  AnsiString Location;
15835  AnsiString ServiceAndRepeatNum;
15836  AnsiString AtLocTime;
15837  AnsiString ArrTime;
15838  AnsiString DepTime;
15839  };
15840  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15841  */
15842 
15843  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15844  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15845 
15846  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15847  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15848  int Suffix = 0;
15849  int IteratorNumber = 0;
15850  AnsiString AnsiSuffix = "";
15851  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15852  {
15853  IteratorNumber++; //first value in loop is 1
15854  Suffix = 0;
15855  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15856  {
15857  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15858  {
15859  Suffix++; //first value is 1
15860  AnsiSuffix = AnsiString(Suffix);
15861  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15862  }
15863  }
15864  }
15865 
15866  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15867  TServiceCallingLocsList ServiceCallingLocsList;
15868  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15869  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15870  {
15871  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15872  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15873  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15874  ServiceCallingLocsList.clear();
15875  if(ActionVector.empty())
15876  {
15877  continue;
15878  }
15879  if(ActionVector.at(0).SignallerControl)
15880  {
15881  continue;
15882  }
15883  for(unsigned int z = 0; z < ActionVector.size(); z++)
15884  {
15885  TActionVectorEntry AVE = ActionVector.at(z);
15886  if(AVE.FormatType == StartNew)
15887  {
15888  if(AVE.LocationType == AtLocation) //located Snt
15889  {
15890  ServiceCallingLocsList.push_back(AVE.LocationName);
15891  }
15892  else //unlocated Snt (could be entering at continuation)
15893  {
15895  if(TE.ActiveTrackElementName != "")
15896  {
15897  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15898  }
15899  else
15900  {
15901  int HLoc = TE.HLoc;
15902  int VLoc = TE.VLoc;
15903  AnsiString HString;
15904  AnsiString VString;
15905  if(HLoc < 0)
15906  {
15907  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15908  }
15909  else
15910  {
15911  HString = AnsiString(HLoc);
15912  }
15913  if(VLoc < 0)
15914  {
15915  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15916  }
15917  else
15918  {
15919  VString = AnsiString(VLoc);
15920  }
15921  ServiceCallingLocsList.push_back(HString + '-' + VString);
15922  }
15923  }
15924  }
15925  else if(AVE.SequenceType == Start) //other start entries, all located
15926  {
15927  ServiceCallingLocsList.push_back(AVE.LocationName);
15928  }
15929  else if(AVE.FormatType == TimeLoc) //z must be > 0
15930  {
15931  if(ServiceCallingLocsList.back() != AVE.LocationName)
15932  {
15933  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15934  }
15935  }
15936  else if(AVE.FormatType == PassTime)
15937  {
15938  ServiceCallingLocsList.push_back(AVE.LocationName);
15939  }
15940  else if(AVE.FormatType == TimeTimeLoc)
15941  {
15942  ServiceCallingLocsList.push_back(AVE.LocationName);
15943  }
15944  else if(AVE.Command == "cdt") //list if not next to start or finish
15945  {
15946  if(ActionVector.at(z-1).SequenceType == Start)
15947  {
15948  continue;
15949  }
15950  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
15951  {
15952  continue;
15953  }
15954  else
15955  {
15956  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
15957  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
15958  }
15959  }
15960  else if(AVE.FormatType == ExitRailway) //Fer
15961  {
15962  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
15963  AnsiString LName = TE.ActiveTrackElementName;
15964  if(LName != "")
15965  {
15966  ServiceCallingLocsList.push_back(LName);
15967  }
15968  else
15969  {
15970  int HLoc = TE.HLoc;
15971  int VLoc = TE.VLoc;
15972  AnsiString HString;
15973  AnsiString VString;
15974  if(HLoc < 0)
15975  {
15976  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15977  }
15978  else
15979  {
15980  HString = AnsiString(HLoc);
15981  }
15982  if(VLoc < 0)
15983  {
15984  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15985  }
15986  else
15987  {
15988  VString = AnsiString(VLoc);
15989  }
15990  ServiceCallingLocsList.push_back(HString + '-' + VString);
15991  }
15992  }
15993  }
15994  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
15995  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
15996  }
15997  //AllServiceCallingLocsMap built
15998 
15999  //test validity of AllServiceCallingLocsMap
16000 /*
16001  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16002  std::ofstream Test(TestFile.c_str());
16003 
16004  if(TestFile == 0)
16005  {
16006  ShowMessage("TestFile failed to open - can't be created");
16007  Utilities->CallLogPop();
16008  return false;
16009  }
16010 
16011  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16012  {
16013  Test << ASCLIt->first << '\n'; //service ref
16014  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16015  {
16016  Test << *SCLIt << '\n';
16017  }
16018  Test << "\n\n";
16019  }
16020  Test.close();
16021  Utilities->CallLogPop();
16022  return true;
16023 */
16024 
16025  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16026  if(TrainDataVector.empty())
16027  {
16028  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16029  Utilities->CallLogPop(2209);
16030  return(false);
16031  }
16032  TLocServiceTimes TLSTEntry;
16033  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16034  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16035  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16036  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16037  LastTTTime = "";
16038 
16039  //calculate LastTTTime
16040  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16041  {
16042  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16043  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16044  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16045  TDateTime LastTDTime;
16046  int IncMinutes = 0;
16047  NumTrains = TrainDataEntry.NumberOfTrains;
16048  if(ActionVector.empty())
16049  {
16050  continue;
16051  }
16052  if(ActionVector.at(0).SignallerControl)
16053  {
16054  continue;
16055  }
16056  if(AVLast->FormatType == Repeat)
16057  {
16058  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16059  AVLast--; //now points to the command before the repeat
16060  }
16061  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16062  {
16063  AVLast--; //points to last timed entry
16064  }
16065  //here AVLast points to last entry with a time
16066  if(AVLast->ArrivalTime != TDateTime(-1))
16067  {
16068  LastTDTime = AVLast->ArrivalTime;
16069  }
16070  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16071  {
16072  LastTDTime = AVLast->EventTime;
16073  }
16074  else
16075  {
16076  continue; //shouldn't ever reach here but if do then skip this service
16077  }
16078  if(NumTrains == 1)
16079  {
16080  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16081  }
16082  else
16083  {
16084  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16085  }
16086  if(LastAnsiTime > LastTTTime)
16087  {
16088  LastTTTime = LastAnsiTime;
16089  }
16090  }
16091 
16092 //build LocServiceTimesVector
16093 
16094 /* struct TLocServiceTimes
16095  {
16096  AnsiString Location;
16097  AnsiString ServiceAndRepeatNum;
16098  AnsiString AtLocTime;
16099  AnsiString ArrTime;
16100  AnsiString DepTime;
16101  };
16102  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16103 
16104 This works as follows:
16105 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16106 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16107 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16108 
16109 Every action for every train is examined and times entered as follows:-
16110 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16111 b) an unlocated Snt: entry time becomes DepTime
16112 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16113 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16114 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16115 f) TimeTimeLoc: Arrival time entered as ErrTime, a check if Arr & Dep same and if s go in as one entry, else all minutes between entered as AtLocs then DepTime
16116 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16117 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16118 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16119 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16120 */
16121  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16122  {
16123  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16124  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16125  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16126  int IncMinutes = 0;
16127  NumTrains = TrainDataEntry.NumberOfTrains;
16128  if(ActionVector.empty())
16129  {
16130  continue;
16131  }
16132  if(ActionVector.at(0).SignallerControl)
16133  {
16134  continue;
16135  }
16136  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16137  {
16138  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16139  }
16140  for(int y = 0; y < NumTrains; y++)
16141  {
16142  if(NumTrains == 1)
16143  {
16144  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16145  }
16146  else if(y == 0)
16147  {
16148  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16149  }
16150  else
16151  {
16152  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16153  }
16154  for(unsigned int z = 0; z < ActionVector.size(); z++)
16155  {
16156  TActionVectorEntry AVE = ActionVector.at(z);
16157  TLSTEntry.AtLocTime = "";
16158  TLSTEntry.ArrTime = "";
16159  TLSTEntry.DepTime = "";
16160  TLSTEntry.Location = "";
16161  TLSTEntry.FrhMarker = "";
16162 
16163  if(AVE.FormatType == StartNew) //Snt only
16164  {
16165  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16166  {
16167  TLSTEntry.Location = AVE.LocationName;
16168  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16169  LocServiceTimesVector.push_back(TLSTEntry);
16170 
16171  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16172  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16173  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16174  {
16175  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16176  {
16177  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16178  break;
16179  }
16180  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16181  {
16182  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16183  break;
16184  }
16185  }
16186  if(FoundStopTime == "")
16187  {
16188  throw Exception("Failure to determine FoundStopTime for located Snt");
16189  }
16190  int WhileCount = 0;
16191  while(true)
16192  {
16193  //add minutes until reach FoundStopTime but don't add that time
16194  WhileCount++;
16195  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16196  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16197  TLSTEntry.DepTime = "";
16198  TLSTEntry.ArrTime = "";
16199  if(IncTime >= FoundStopTime) //don't add that time
16200  {
16201  break;
16202  }
16203  LocServiceTimesVector.push_back(TLSTEntry);
16204  if(WhileCount > 2000)
16205  {
16206  throw Exception("While loop failed to break in 2000 loops for located Snt");
16207  }
16208  }
16209  }
16210  else //unlocated Snt, use the EventTime as DepTime for this vector
16211  {
16213  if(TE.ActiveTrackElementName != "")
16214  {
16215  TLSTEntry.Location = TE.ActiveTrackElementName;
16216  }
16217  else
16218  {
16219  int HLoc = TE.HLoc;
16220  int VLoc = TE.VLoc;
16221  AnsiString HString;
16222  AnsiString VString;
16223  if(HLoc < 0)
16224  {
16225  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16226  }
16227  else
16228  {
16229  HString = AnsiString(HLoc);
16230  }
16231  if(VLoc < 0)
16232  {
16233  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16234  }
16235  else
16236  {
16237  VString = AnsiString(VLoc);
16238  }
16239  TLSTEntry.Location = HString + '-' + VString;
16240  }
16241  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16242  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16243  LocServiceTimesVector.push_back(TLSTEntry);
16244  }
16245  }
16246 
16247  else if(AVE.SequenceType == Start) //other start entries, all located
16248  {
16249  TLSTEntry.Location = AVE.LocationName;
16250  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16251  LocServiceTimesVector.push_back(TLSTEntry);
16252  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16253  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16254  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16255  {
16256  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16257  {
16258  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16259  break;
16260  }
16261  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16262  {
16263  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16264  break;
16265  }
16266  }
16267  if(FoundStopTime == "")
16268  {
16269  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16270  }
16271  int WhileCount = 0;
16272  while(true)
16273  {
16274  //add minutes until reach FoundStopTime but don't add that time
16275  WhileCount++;
16276  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16277  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16278  TLSTEntry.DepTime = "";
16279  TLSTEntry.ArrTime = "";
16280  if(IncTime >= FoundStopTime) //don't add that time
16281  {
16282  break;
16283  }
16284  LocServiceTimesVector.push_back(TLSTEntry);
16285  if(WhileCount > 2000)
16286  {
16287  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16288  }
16289  }
16290  }
16291 
16292  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if ar5rival add in all mins to the departure or finish
16293  {
16294  TLSTEntry.Location = AVE.LocationName;
16295  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16296  {
16297  bool SkipAddingMinutes = false;
16298  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16299  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16300  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16301  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16302  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16303  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16304  {
16305  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16306  {
16307  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16308  break;
16309  }
16310  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16311  {
16312  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16313  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16314  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16315  {
16316  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16317  SkipAddingMinutes = true;
16318  }
16319  break;
16320  }
16321  }
16322  if(FoundStopTime == "")
16323  {
16324  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16325  }
16326  if(!SkipAddingMinutes)
16327  {
16328  int WhileCount = 0;
16329  while(true)
16330  {
16331  //add minutes until reach FoundStopTime but don't add that time
16332  WhileCount++;
16333  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16334  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16335  TLSTEntry.DepTime = "";
16336  TLSTEntry.ArrTime = "";
16337  if(IncTime >= FoundStopTime) //don't add that time
16338  {
16339  break;
16340  }
16341  LocServiceTimesVector.push_back(TLSTEntry);
16342  if(WhileCount > 2000)
16343  {
16344  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16345  }
16346  }
16347  }
16348  }
16349  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16350  {
16351  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16352  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16353  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16354  {
16355  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16356  {
16357  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16358  LocServiceTimesVector.pop_back();
16359  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16360  }
16361  else //just add the dep & atloc times
16362  {
16363  TLSTEntry.ArrTime = "";
16364  LocServiceTimesVector.push_back(TLSTEntry);
16365  }
16366  }
16367  else //just add the dep & atloc times
16368  {
16369  TLSTEntry.ArrTime = "";
16370  LocServiceTimesVector.push_back(TLSTEntry);
16371  }
16372  }
16373  }
16374 
16375  else if(AVE.FormatType == TimeTimeLoc)
16376  {
16377  TLSTEntry.Location = AVE.LocationName;
16378  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16379  {
16380  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16381  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16382  }
16383  if(AVE.DepartureTime > TDateTime(-1)) //should be
16384  {
16385  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16386  }
16387  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16388  {
16389  LocServiceTimesVector.push_back(TLSTEntry);
16390  }
16391  else
16392  {
16393  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16394  TLSTEntry.DepTime = "";
16395  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16396  TLSTEntry.ArrTime = ""; //done with this now
16397  while(TLSTEntry.AtLocTime < TempDepTime)
16398  {
16399  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16400  if(TLSTEntry.AtLocTime == TempDepTime)
16401  {
16402  TLSTEntry.DepTime = TempDepTime; //restore value
16403  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16404  }
16405  else
16406  {
16407  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16408  }
16409  }
16410  }
16411  }
16412 
16413  else if(AVE.FormatType == ExitRailway) //Fer
16414  {
16415  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16416  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16417  if(LName != "")
16418  {
16419  TLSTEntry.Location = LName;
16420  }
16421  else
16422  {
16423  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
16424  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
16425  AnsiString HString;
16426  AnsiString VString;
16427  if(HLoc < 0)
16428  {
16429  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16430  }
16431  else
16432  {
16433  HString = AnsiString(HLoc);
16434  }
16435  if(VLoc < 0)
16436  {
16437  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16438  }
16439  else
16440  {
16441  VString = AnsiString(VLoc);
16442  }
16443  TLSTEntry.Location = HString + '-' + VString;
16444  }
16445  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
16446  }
16447 
16448  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
16449  {
16450  AnsiString FrhTime;
16451  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16452  {
16453  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16454  }
16455  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16456  {
16457  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16458  }
16459  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
16460  TLSTEntry.Location = AVE.LocationName;
16461  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16462  TLSTEntry.FrhMarker = "Frh";
16463  LocServiceTimesVector.push_back(TLSTEntry);
16464  TLSTEntry.FrhMarker = "";
16465  //add all times from next minute to end of timetable
16466  while(IncTime <= LastTTTime)
16467  {
16468  TLSTEntry.AtLocTime = IncTime;
16469  LocServiceTimesVector.push_back(TLSTEntry);
16470  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16471  }
16472  }
16473 
16474  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
16475  {
16476  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
16477  {
16478  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
16479  TLSTEntry.Location = AVE.LocationName;
16480  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16481  TLSTEntry.FrhMarker = "Frh";
16482  LocServiceTimesVector.push_back(TLSTEntry);
16483  TLSTEntry.FrhMarker = "";
16484  //add all times from next minute to end of timetable
16485  while(IncTime <= LastTTTime)
16486  {
16487  TLSTEntry.AtLocTime = IncTime;
16488  LocServiceTimesVector.push_back(TLSTEntry);
16489  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
16490  }
16491  }
16492  }
16493 
16494  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
16495  {
16496  //nothing is done here as the entry will be listed at this time under the new service reference
16497  }
16498  }
16499  }
16500  }
16501 
16502  //now sort in location order
16503  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
16504  //LocServiceTimesVector now complete & sorted in location order
16505 
16506  //declare pointers for use in printouts
16507  TLocServiceTimesVector::iterator Ptr1, Ptr2;
16508 
16509  //set up the output file
16510  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16511  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16512 
16513  std::ofstream TTFile3(TTFileName3.c_str());
16514 
16515  if(TTFile3 == 0)
16516  {
16517  ShowMessage("Conflict Analysis file failed to open - can't be created");
16518  Utilities->CallLogPop(2210);
16519  return(false);
16520  }
16521  if(LocServiceTimesVector.empty())
16522  {
16523  ShowMessage("No timetabled services found");
16524  TTFile3.close();
16525  DeleteFile(TTFileName3);
16526  Utilities->CallLogPop(2211);
16527  return(false);
16528  }
16529  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
16530 
16531 
16532  //arrivals
16533  if(ArrChecked)
16534  {
16535  //sort in ArrTime order for each location
16536  Ptr1 = LocServiceTimesVector.begin();
16537  Ptr2 = Ptr1 + 1;
16538  while(Ptr2 != LocServiceTimesVector.end())
16539  {
16540  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16541  {
16542  Ptr2++;
16543  if(Ptr2 == LocServiceTimesVector.end())
16544  {
16545  break;
16546  }
16547  }
16548  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
16549  Ptr1 = Ptr2; //first entry with next name
16550  if(Ptr2 != LocServiceTimesVector.end())
16551  {
16552  Ptr2++;
16553  }
16554  }
16555 
16556  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
16557 
16558  TTFile3 << "Arrival analysis: an asterisk means that the number of same approach code arrivals is equal to or greater than the number of platforms.\n";
16559  TTFile3 << "If the total number of arrivals exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16560  MinuteString = " minutes";
16561  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16562  if(ArrRange == 1)
16563  {
16564  MinuteString = " minute";
16565  }
16566  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
16567  TTFile3 << ",Platforms,Trains\n\n";
16568 
16569  Ptr1 = LocServiceTimesVector.begin();
16570  Ptr2 = Ptr1 + 1;
16571  while(Ptr2 != LocServiceTimesVector.end())
16572  {
16573  PreviousService = "";
16574  NumTrainsAtLoc = 0;
16575  ServiceAndRepeatNumTotal = "";
16576  NumPlats = 0;
16577  NumPlatsAtThisLocCalculated = false;
16578  BasicTime = "";
16579  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16580  {
16581  PreviousService = "";
16582  NumTrainsAtLoc = 0;
16583  ServiceAndRepeatNumTotal = "";
16584  NumPlats = 0;
16585  NumPlatsAtThisLocCalculated = false;
16586  BasicTime = "";
16587  Ptr1++;
16588  Ptr2++;
16589  if(Ptr2 == LocServiceTimesVector.end())
16590  {
16591  break;
16592  }
16593  }
16594  if(Ptr2 == LocServiceTimesVector.end())
16595  {
16596  break;
16597  }
16598  while(Ptr2->Location == Ptr1->Location)
16599  {
16600  PreviousService = "";
16601  NumTrainsAtLoc = 0;
16602  ServiceAndRepeatNumTotal = "";
16603  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16604  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16605  {
16606  break;
16607  }
16608  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
16609  {
16610  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
16611  Ptr1++;
16612  Ptr2++;
16613  if(Ptr2 == LocServiceTimesVector.end())
16614  {
16615  break;
16616  }
16617  if(Ptr2->Location != Ptr1->Location)
16618  {
16619  break;
16620  }
16621  }
16622  if(Ptr2 == LocServiceTimesVector.end())
16623  {
16624  break;
16625  }
16626  if(Ptr2->Location != Ptr1->Location)
16627  {
16628  break;
16629  }
16630  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
16631  {
16632  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
16633  {
16634  break;
16635  }
16636  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16637  {
16638  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
16639  NumPlatsAtThisLocCalculated = true;
16640  }
16641  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16642  {
16643  if(ServiceAndRepeatNumTotal == "")
16644  {
16645  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16646  NumTrainsAtLoc = 1;
16647  }
16648  else
16649  {
16650  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
16651  }
16652  }
16653  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16654  if(ServiceAndRepeatNumTotal == "")
16655  {
16656  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16657  NumTrainsAtLoc = 1;
16658  }
16659  else
16660  {
16661  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
16662  }
16663  Ptr1 = Ptr2;
16664  Ptr2++;
16665  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
16666  {
16667  int MaxNumberOfSameDirections = 0;
16668  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16669  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16670  {
16671 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16672  TTFile3.close();
16673  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16674 // Utilities->CallLogPop(2224);
16675 // return false;
16676  }
16677  AnsiString Asterisk = "";
16678  if(MaxNumberOfSameDirections >= NumPlats)
16679  {
16680  Asterisk = "* ";
16681  }
16682  //print out a single line for number of trains at loc with all service refs
16683  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16684  ArrivalsPrinted = true;
16685  ServiceAndRepeatNumTotal = "";
16686  }
16687  if(Ptr2 == LocServiceTimesVector.end())
16688  {
16689  break;
16690  }
16691  if(Ptr2->Location != Ptr1->Location)
16692  {
16693  break;
16694  }
16695  }
16696  if(Ptr2 == LocServiceTimesVector.end())
16697  {
16698  break;
16699  }
16700  }
16701  }
16702  if(!ArrivalsPrinted)
16703  {
16704  TTFile3 << "Nothing to report for arrivals";
16705  }
16706  TTFile3 << "\n\n\n";
16707  }
16708  //end of routine for arrivals
16709 
16710  //departures
16711  if(DepChecked)
16712  {
16713  //sort in DepTime order for each location
16714  Ptr1 = LocServiceTimesVector.begin();
16715  Ptr2 = Ptr1 + 1;
16716  while(Ptr2 != LocServiceTimesVector.end())
16717  {
16718  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16719  {
16720  Ptr2++;
16721  if(Ptr2 == LocServiceTimesVector.end())
16722  {
16723  break;
16724  }
16725  }
16726  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
16727  Ptr1 = Ptr2; //first entry with next name
16728  if(Ptr2 != LocServiceTimesVector.end())
16729  {
16730  Ptr2++;
16731  }
16732  }
16733 
16734  //routine for departures - number of trains departing within the specified range with services listed at the end
16735  TTFile3 << "Departure analysis: an asterisk means that the number of same exit code departures is equal to or greater than the number of platforms.\n";
16736  TTFile3 << "If the total number of departures exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
16737  MinuteString = " minutes";
16738  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16739  if(DepRange == 1)
16740  {
16741  MinuteString = " minute";
16742  }
16743  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
16744  TTFile3 << ",Platforms,Trains\n\n";
16745 
16746  Ptr1 = LocServiceTimesVector.begin();
16747  Ptr2 = Ptr1 + 1;
16748  while(Ptr2 != LocServiceTimesVector.end())
16749  {
16750  PreviousService = "";
16751  NumTrainsAtLoc = 0;
16752  ServiceAndRepeatNumTotal = "";
16753  NumPlats = 0;
16754  NumPlatsAtThisLocCalculated = false;
16755  BasicTime = "";
16756  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16757  {
16758  PreviousService = "";
16759  NumTrainsAtLoc = 0;
16760  ServiceAndRepeatNumTotal = "";
16761  NumPlats = 0;
16762  NumPlatsAtThisLocCalculated = false;
16763  BasicTime = "";
16764  Ptr1++;
16765  Ptr2++;
16766  if(Ptr2 == LocServiceTimesVector.end())
16767  {
16768  break;
16769  }
16770  }
16771  if(Ptr2 == LocServiceTimesVector.end())
16772  {
16773  break;
16774  }
16775  while(Ptr2->Location == Ptr1->Location)
16776  {
16777  PreviousService = "";
16778  NumTrainsAtLoc = 0;
16779  ServiceAndRepeatNumTotal = "";
16780  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
16781  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16782  {
16783  break;
16784  }
16785  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
16786  {
16787  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
16788  Ptr1++;
16789  Ptr2++;
16790  if(Ptr2 == LocServiceTimesVector.end())
16791  {
16792  break;
16793  }
16794  if(Ptr2->Location != Ptr1->Location)
16795  {
16796  break;
16797  }
16798  }
16799  if(Ptr2 == LocServiceTimesVector.end())
16800  {
16801  break;
16802  }
16803  if(Ptr2->Location != Ptr1->Location)
16804  {
16805  break;
16806  }
16807  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
16808  {
16809  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
16810  {
16811  break;
16812  }
16813  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16814  {
16815  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
16816  NumPlatsAtThisLocCalculated = true;
16817  }
16818  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16819  {
16820  if(ServiceAndRepeatNumTotal == "")
16821  {
16822  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16823  NumTrainsAtLoc = 1;
16824  }
16825  else
16826  {
16827  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16828  }
16829  }
16830  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16831  if(ServiceAndRepeatNumTotal == "")
16832  {
16833  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16834  NumTrainsAtLoc = 1;
16835  }
16836  else
16837  {
16838  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16839  }
16840  Ptr1 = Ptr2;
16841  Ptr2++;
16842  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
16843  {
16844  int MaxNumberOfSameDirections = 0;
16845  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16846  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16847  {
16848 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16849  TTFile3.close();
16850  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16851 // Utilities->CallLogPop(2225);
16852 // return false;
16853  }
16854  AnsiString Asterisk = "";
16855  if(MaxNumberOfSameDirections >= NumPlats)
16856  {
16857  Asterisk = "* ";
16858  }
16859  //print out a single line for number of trains at loc with all service refs
16860  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16861  DeparturesPrinted = true;
16862  ServiceAndRepeatNumTotal = "";
16863  }
16864  if(Ptr2 == LocServiceTimesVector.end())
16865  {
16866  break;
16867  }
16868  if(Ptr2->Location != Ptr1->Location)
16869  {
16870  break;
16871  }
16872  }
16873  if(Ptr2 == LocServiceTimesVector.end())
16874  {
16875  break;
16876  }
16877  }
16878  }
16879  if(!DeparturesPrinted)
16880  {
16881  TTFile3 << "Nothing to report for departures";
16882  }
16883  TTFile3 << "\n\n\n";
16884  }
16885  //end of routine for departures
16886 
16887 
16888  //list trains at locations at same time
16889 
16890  if(AtLocChecked)
16891  {
16892  //sort in AtLocTime order for each location
16893  Ptr1 = LocServiceTimesVector.begin();
16894  Ptr2 = Ptr1 + 1;
16895  while(Ptr2 != LocServiceTimesVector.end())
16896  {
16897  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16898  {
16899  Ptr2++;
16900  if(Ptr2 == LocServiceTimesVector.end())
16901  {
16902  break;
16903  }
16904  }
16905  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16906  Ptr1 = Ptr2; //first entry with next name
16907  if(Ptr2 != LocServiceTimesVector.end())
16908  {
16909  Ptr2++;
16910  }
16911  }
16912 
16913  //print out simultaneous AtLocs (don't need range of times for AtLocs)
16914  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
16915  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
16916  TTFile3 << ",Platforms,Trains,\n\n";
16917  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16918  Ptr1 = LocServiceTimesVector.begin();
16919  Ptr2 = Ptr1 + 1;
16920  while(Ptr2 != LocServiceTimesVector.end())
16921  {
16922  PreviousService = "";
16923  ServiceAndRepeatNumTotal = "";
16924  NumTrainsAtLoc = 0;
16925  NumPlats = 0;
16926  NumPlatsAtThisLocCalculated = false;
16927  FrhCount = 0;
16928  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16929  {
16930  PreviousService = "";
16931  ServiceAndRepeatNumTotal = "";
16932  NumTrainsAtLoc = 0;
16933  NumPlats = 0;
16934  NumPlatsAtThisLocCalculated = false;
16935  FrhCount = 0;
16936  Ptr1++;
16937  Ptr2++;
16938  if(Ptr2 == LocServiceTimesVector.end())
16939  {
16940  break;
16941  }
16942  }
16943  if(Ptr2 == LocServiceTimesVector.end())
16944  {
16945  break;
16946  }
16947  while(Ptr2->Location == Ptr1->Location)
16948  {
16949  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
16950  {
16951  FrhCount++;
16952  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16953  }
16954  PreviousService = "";
16955  NumTrainsAtLoc = 0;
16956  ServiceAndRepeatNumTotal = "";
16957  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16958  {
16959  break;
16960  }
16961  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
16962  {
16963  Ptr1++;
16964  if(Ptr1->FrhMarker == "Frh")
16965  {
16966  FrhCount++;
16967  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16968  }
16969  Ptr2++;
16970  if(Ptr2 == LocServiceTimesVector.end())
16971  {
16972  break;
16973  }
16974  if(Ptr2->Location != Ptr1->Location)
16975  {
16976  break;
16977  }
16978  }
16979  if(Ptr2 == LocServiceTimesVector.end())
16980  {
16981  break;
16982  }
16983  if(Ptr2->Location != Ptr1->Location)
16984  {
16985  break;
16986  }
16987  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
16988  {
16989  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
16990  {
16991  break;
16992  }
16993  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16994  {
16995  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
16996  NumPlatsAtThisLocCalculated = true;
16997  }
16998  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16999  {
17000  if(ServiceAndRepeatNumTotal == "")
17001  {
17002  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17003  NumTrainsAtLoc = 1;
17004  }
17005  else
17006  {
17007  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17008  }
17009  }
17010  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17011  if(ServiceAndRepeatNumTotal == "")
17012  {
17013  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17014  NumTrainsAtLoc = 1;
17015  }
17016  else
17017  {
17018  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17019  }
17020  Ptr1 = Ptr2;
17021  if(Ptr1->FrhMarker == "Frh")
17022  {
17023  FrhCount++;
17024  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17025  }
17026  Ptr2++;
17027  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17028  {
17029 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17030 //new text //don't print out if all remainers or if only 1 train at loc
17031  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17032 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17033 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17034  {
17035  AnsiString Asterisk = "";
17036  if(NumTrainsAtLoc > NumPlats)
17037  {
17038  Asterisk = "* ";
17039  }
17040  //print out a single line for number of trains at loc with all service refs
17041  if(FrhCount == 0)
17042  {
17043  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17044  }
17045  else if(FrhCount == 1)
17046  {
17047  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17048  }
17049  else
17050  {
17051  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17052  }
17053  LastFrhCount = FrhCount;
17054  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17055  AtLocsPrinted = true;
17056  ServiceAndRepeatNumTotal = "";
17057  }
17058  }
17059  if(Ptr2 == LocServiceTimesVector.end())
17060  {
17061  break;
17062  }
17063  if(Ptr2->Location != Ptr1->Location)
17064  {
17065  break;
17066  }
17067  }
17068  if(Ptr2 == LocServiceTimesVector.end())
17069  {
17070  break;
17071  }
17072  }
17073  }
17074  if(!AtLocsPrinted)
17075  {
17076  TTFile3 << "Nothing to report for trains at locations";
17077  }
17078  TTFile3 << "\n\n\n";
17079  //end of simultaneous AtLocs
17080 
17081 /*
17082  //print out the full vector here for testing purposes
17083  TTFile3 << "Full LocServiceTimesVector\n\n";
17084  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17085 
17086  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17087  {
17088  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17089  }
17090 
17091  TTFile3 << "\n\n\n";
17092 */
17093  }
17094  TTFile3.close();
17095  Utilities->CallLogPop(2212);
17096  return(true);
17097  }
17098 
17099  catch(const Exception &e) //non error catch
17100  {
17101  AnsiString TTErrorFileName = "Analysis Error.txt";
17102  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
17103  std::ofstream TTError(TTErrorFileName.c_str());
17104  if(TTError == 0)
17105  {
17106  ShowMessage("Analysis error file failed to open - can't be created");
17107  Utilities->CallLogPop(2233);
17108  return(false);
17109  }
17110  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17111  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
17112  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
17113  TTError.close();
17114  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
17115  Utilities->CallLogPop(2226);
17116  return(false);
17117  }
17118 }
17119 
17120 // ---------------------------------------------------------------------------
17121 
17122 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
17123 {
17124 //convert times to integer minutes
17125  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
17126  if((Time1 == "") || (Time2 == ""))
17127  {
17128  Utilities->CallLogPop(2213);
17129  return(false);
17130  }
17131  int Mins = Time1.SubString(4,2).ToInt();
17132  int Hours = Time1.SubString(1,2).ToInt();
17133  int Time1Mins = (Hours * 60) + Mins;
17134  Mins = Time2.SubString(4,2).ToInt();
17135  Hours = Time2.SubString(1,2).ToInt();
17136  int Time2Mins = (Hours * 60) + Mins;
17137  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
17138  {
17139  Utilities->CallLogPop(2214);
17140  return(true);
17141  }
17142  Utilities->CallLogPop(2215);
17143  return(false);
17144 }
17145 
17146 // ---------------------------------------------------------------------------
17147 
17148 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
17149  bool &AnalysisError, int &MaxNumberOfSameDirections)
17150 {
17151  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
17152 
17153  try
17154  {
17155  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
17156  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
17157  int SCPos = 0;
17158  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
17159  //first change every second comma in Input to a semicolon so can separate services but keep times with services
17160  bool EvenComma = false;
17161  for(int x = 1; x <= Input.Length(); x++)
17162  {
17163  TempStr1 = Input[x];
17164  if(TempStr1 == AnsiString(',') && EvenComma)
17165  {
17166  TempStr2 += ';';
17167  }
17168  else
17169  {
17170  TempStr2 += Input[x];
17171  }
17172  if(TempStr1 == AnsiString(','))
17173  {
17174  EvenComma = !EvenComma;
17175  }
17176  }
17177  //load up the list of services with associated times
17178  while(TempStr2.Length() > 0)
17179  {
17180  SCPos = TempStr2.Pos(';');
17181  if(SCPos > 0) //0 if not found, as won't be when only one service left
17182  {
17183  OneService = TempStr2.SubString(1, SCPos - 1);
17184  ServiceList.push_back(OneService);
17185  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
17186  }
17187  else //no semicolon so looking at last (or only) element
17188  {
17189  ServiceList.push_back(TempStr2);
17190  TempStr2 = "";
17191  }
17192  }
17193  ServiceList.sort(); // alphabetical order
17194  ServiceList.unique(); //remove duplicates
17195  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17196 
17197  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
17198  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
17199  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
17200  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
17201 
17202  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17203  {
17204  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
17205  }
17206  SLIt3 = ServiceList.end();
17207  SLIt3--; //so points to last element
17208  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
17209  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
17210  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
17211  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
17212  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
17213  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
17214 
17215  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
17216  {
17217  SLIt = SLIt1;
17218  SLIt++; //so points to one after SLIt1
17219  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
17220  {
17221  continue; //already allocated so skip to the next
17222  }
17223  else
17224  {
17225  CommaPos1 = SLIt1->Pos(','); //can't be 0
17226  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
17227  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
17228  SpacePos = ServiceRef1.Pos(' ');
17229  RepeatNum1 = 0;
17230  if(SpacePos > 0) //otherwise it's already correct
17231  {
17232  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17233  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
17234  if(RepeatInfo1[1] == 'F')
17235  {
17236  RepeatNum1 = 0;
17237  }
17238  else
17239  {
17240  SpacePos = RepeatInfo1.Pos(' ');
17241  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
17242  }
17243  }
17244  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
17245  //but this includes the "&0" etc so need to strip these
17246  AmpersandPos = AnsiTime1.Pos('&');
17247  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
17248 
17249  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
17250  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
17251  {
17252  throw Exception("ASCLIt1 Error in " + Input);
17253  }
17254  ServiceCallingLocsList1 = ASCLIt1->second;
17255  AmpersandPos = SLIt1->Pos('&');
17256  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17257  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
17258 
17259  SameDirectionCount = 1;
17260  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
17261  {
17262  CommaPos2 = SLIt2->Pos(','); //can't be 0
17263  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
17264  //but this contains "(First service..." etc so need to strip these
17265  SpacePos = ServiceRef2.Pos(' ');
17266  RepeatNum2 = 0;
17267  if(SpacePos > 0) //otherwise it's already correct
17268  {
17269  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
17270  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
17271  if(RepeatInfo2[1] == 'F')
17272  {
17273  RepeatNum2 = 0;
17274  }
17275  else
17276  {
17277  SpacePos = RepeatInfo2.Pos(' ');
17278  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
17279  }
17280  }
17281  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
17282  //but this includes the "&0" etc so need to strip these
17283  AmpersandPos = AnsiTime2.Pos('&');
17284  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
17285 
17286  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
17287  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
17288  {
17289  throw Exception("ASCLIt2 Error in " + Input);
17290  }
17291  ServiceCallingLocsList2 = ASCLIt2->second;
17292  //now compare the two
17293  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
17294  {
17295  int AmpersandPos = SLIt2->Pos('&');
17296  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17297  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
17298  SameDirectionCount++;
17299  }
17300  }
17301  if(SameDirectionCount > MaxNumberOfSameDirections)
17302  {
17303  MaxNumberOfSameDirections = SameDirectionCount;
17304  }
17305  }
17306  }
17307 
17308  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
17309  {
17310  //any existing direction so allocate it now
17311  AmpersandPos = SLIt3->Pos('&');
17312  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
17313  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
17314  }
17315  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
17316  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17317  {
17318  //extract the DirectionMarker as an integer
17319  AmpersandPos = SLIt->Pos('&');
17320  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
17321  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
17322  DirectionMarker = DirectionMarkerString.ToInt();
17323  AnsiString DirectionSuffix = "";
17324  char c;
17325  if(DirectionMarker < 27)
17326  {
17327  c = 64 + DirectionMarker; //so 1 -> 'A'
17328  DirectionSuffix = "," + AnsiString(c);
17329  }
17330  else if(DirectionMarker < 53)
17331  {
17332  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
17333  DirectionSuffix = ",A" + AnsiString(c);
17334  }
17335  else
17336  {
17337  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
17338  }
17339  *SLIt = ServiceWithoutMarker + DirectionSuffix;
17340  }
17341  //now prepare the final consolidated output
17342  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17343  {
17344  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17345  }
17346  if(Output.Length() > 0)
17347  {
17348  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17349  }
17350  Utilities->CallLogPop(2216);
17351  return(Output);
17352  }
17353 
17354  catch(const Exception &e) //non error catch
17355  {
17356  AnalysisError = true;
17357  Utilities->CallLogPop(2227);
17358  return(e.Message);
17359  }
17360 }
17361 
17362 // ---------------------------------------------------------------------------
17363 
17364 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
17365 {
17366  //similar to above but doesn't include times in the input
17367  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
17368  AnsiString InternalInput = Input, Output = "", OneService = "";
17369  int CommaPos = 0;
17370  std::list<AnsiString> ServiceList;
17371  //load up the list
17372  while(InternalInput.Length() > 0)
17373  {
17374  CommaPos = InternalInput.Pos(',');
17375  if(CommaPos > 0) //0 if not found, as won't be when only one service left
17376  {
17377  OneService = InternalInput.SubString(1, CommaPos - 1);
17378  ServiceList.push_back(OneService);
17379  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
17380  }
17381  else //no comma so looking at last (or only) element
17382  {
17383  ServiceList.push_back(InternalInput);
17384  InternalInput = "";
17385  }
17386  }
17387 
17388  ServiceList.sort(); // alphabetical order
17389  ServiceList.unique(); //remove duplicates
17390  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
17391  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
17392  {
17393  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
17394  }
17395  if(Output.Length() > 0)
17396  {
17397  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
17398  }
17399  Utilities->CallLogPop(2217);
17400  return(Output);
17401 }
17402 
17403 // ---------------------------------------------------------------------------
17404 
17405 
17406 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
17407  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
17408 {
17409  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
17410  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
17411 
17412  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
17413  //general list pointers, LocPtrs point to Location in the two lists
17414 
17415  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
17416  //for List1
17417  bool LocFound = false;
17418  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
17419  int IncMinutes;
17420  TDateTime FirstServiceTime;
17421 
17422  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
17423  int Ref1Target = 0, Ref1Count = 0;
17424  int SlashPos = Ref1.Pos('/');
17425  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
17426  {
17427  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
17428  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17429  }
17430  int Ref2Target = 0, Ref2Count = 0;
17431  SlashPos = Ref2.Pos('/');
17432  if(SlashPos > 0) //if 0 leave as is
17433  {
17434  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
17435  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
17436  }
17437  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
17438  {
17439  //even if others have same names. But if there are cdt's then need to refind the correct service
17440  if((*ListPtr1) == Location) //
17441  {
17442  LocPtr1 = ListPtr1; //may be modified later
17443  LocFound = true;
17444  }
17445  if(ListPtr1->SubString(1, 3) == "%%%")
17446  {
17447  AnsiString CDTTime = ListPtr1->SubString(4, 5);
17448  //now adjust the time to correspond to the repeat if there is one
17449  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
17450  {
17451  IncMinutes = -1;
17452  FirstServiceTime = TDateTime(-1);
17453  bool BreakFlag = false;
17454  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17455  {
17456  if(TDVIt->ServiceReference == Ref1)
17457  {
17458  if(Ref1Target > Ref1Count)
17459  {
17460  Ref1Count++;
17461  continue;
17462  }
17463  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17464  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17465  {
17466  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17467  {
17468  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
17469  BreakFlag = true;
17470  break;
17471  }
17472  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
17473  {
17474  FirstServiceTime = AVIt->ArrivalTime;
17475  BreakFlag = true;
17476  break;
17477  }
17478  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17479  {
17480  FirstServiceTime = AVIt->DepartureTime;
17481  BreakFlag = true;
17482  break;
17483  }
17484  }
17485  if(BreakFlag)
17486  {
17487  break;
17488  }
17489  }
17490  }
17491  if(IncMinutes == -1)
17492  {
17493  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17494  }
17495  if(FirstServiceTime == TDateTime(-1))
17496  {
17497  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17498  }
17499  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
17500  }
17501  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
17502  {
17503  LocFound = false;
17504  continue;
17505  }
17506  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
17507  {
17508  break;
17509  }
17510  if(Time1 > CDTTime) //not there yet so go on
17511  {
17512  LocFound = false;
17513  continue;
17514  }
17515  if(Time1 < CDTTime) //gone too far so can stop now
17516  {
17517  break;
17518  }
17519  }
17520  }
17521  if(!LocFound) //have to find it in both lists
17522  {
17523  Utilities->CallLogPop(2228);
17524  return( false);
17525  }
17526  //for List2
17527  LocFound = false;
17528  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
17529  {
17530  if((*ListPtr2) == Location)
17531  {
17532  LocPtr2 = ListPtr2; //may be modified later
17533  LocFound = true;
17534  }
17535  if(ListPtr2->SubString(1, 3) == "%%%")
17536  {
17537  AnsiString CDTTime = ListPtr2->SubString(4, 5);
17538  //now adjust the time to correspond to the repeat if there is one
17539  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
17540  {
17541  IncMinutes = -1;
17542  FirstServiceTime = TDateTime(-1);
17543  bool BreakFlag = false;
17544  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
17545  {
17546  if(TDVIt->ServiceReference == Ref2)
17547  {
17548  if(Ref2Target > Ref2Count)
17549  {
17550  Ref2Count++;
17551  continue;
17552  }
17553  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
17554  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
17555  {
17556  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
17557  {
17558  FirstServiceTime = AVIt->EventTime;
17559  BreakFlag = true;
17560  break;
17561  }
17562  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
17563  {
17564  FirstServiceTime = AVIt->ArrivalTime;
17565  BreakFlag = true;
17566  break;
17567  }
17568  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
17569  {
17570  FirstServiceTime = AVIt->DepartureTime;
17571  BreakFlag = true;
17572  break;
17573  }
17574  }
17575  if(BreakFlag)
17576  {
17577  break;
17578  }
17579  }
17580  }
17581  if(IncMinutes == -1)
17582  {
17583  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17584  }
17585  if(FirstServiceTime == TDateTime(-1))
17586  {
17587  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
17588  }
17589  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
17590  }
17591  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
17592  {
17593  LocFound = false;
17594  continue;
17595  }
17596  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
17597  {
17598  break;
17599  }
17600  if(Time2 > CDTTime) //not there yet so go on
17601  {
17602  LocFound = false;
17603  continue;
17604  }
17605  if(Time2 < CDTTime) //gone too far so can stop now
17606  {
17607  break;
17608  }
17609  }
17610  }
17611  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
17612  {
17613  Utilities->CallLogPop(2229);
17614  return( false);
17615  }
17616  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
17617  //set ListPtr1 to the search start position
17618  if(Arrival)
17619  {
17620  LP1 = List1.begin();
17621  LP1--; //now points to before the first entry
17622  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
17623  {
17624  if(ListPtr1 == List1.begin())
17625  {
17626  break;
17627  }
17628  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
17629  {
17630  ListPtr1++; //point to one past the cdt
17631  break;
17632  }
17633  }
17634  //set ListPtr2 to the search start position
17635  LP2 = List2.begin();
17636  LP2--; //now points to before the first entry
17637  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
17638  {
17639  if(ListPtr2 == List2.begin())
17640  {
17641  break;
17642  }
17643  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
17644  {
17645  ListPtr2++; //point to one past the cdt
17646  break;
17647  }
17648  }
17649  //ListPtr1 & 2 now at search start position
17650  LP1 = ListPtr1;
17651  LP2 = ListPtr2;
17652  //now search forwards, i.e. for common locations before Location
17653  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17654  {
17655  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
17656  {
17657  break;
17658  }
17659  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
17660  {
17661  break;
17662  }
17663  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17664  {
17665  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
17666  {
17667  break;
17668  }
17669  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
17670  {
17671  break;
17672  }
17673  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
17674  {
17675  Utilities->CallLogPop(2230);
17676  return( true);
17677  }
17678  }
17679  }
17680  }
17681 
17682  //now, for the departure analysis, reset the start positions and search locations after Location
17683 
17684  else
17685  {
17686  LP1 = LocPtr1;
17687  LP1++; //start at one past the location itself
17688  LP2 = LocPtr2;
17689  LP2++;
17690  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
17691  {
17692  if(ListPtr1 == List1.end()) //reached end point so stop
17693  {
17694  break;
17695  }
17696  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
17697  {
17698  break;
17699  }
17700  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
17701  {
17702  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
17703  {
17704  break;
17705  }
17706  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
17707  {
17708  break;
17709  }
17710  if((*ListPtr1) == (*ListPtr2)) //found a common later location
17711  {
17712  Utilities->CallLogPop(2231);
17713  return( true);
17714  }
17715  }
17716  }
17717  }
17718  Utilities->CallLogPop(2232);
17719  return( false);
17720 }
17721 
17722 // ---------------------------------------------------------------------------
17723 
17724 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
17725 {
17726  // changed at v2.7.0 to show allowable exit elements
17727  if(ExitList.empty())
17728  {
17729  return("");
17730  }
17731  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
17732  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
17733  AnsiString ExitLocList = "";
17734  AllowedExits = "";
17735 
17736  unsigned int Counter = 0;
17737  for(TExitListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
17738  {
17739  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
17740  Counter++;
17741  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
17742  {
17743  ExitLocList += "\n";
17744  }
17745  }
17746  if(StartName == "")
17747  {
17748  if(ExitList.size() == 1)
17749  {
17750  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
17751  Utilities->CallLogPop(1571);
17752  return(" at " + ID);
17753  }
17754  else
17755  {
17756  Utilities->CallLogPop(1572);
17757  if(ExitList.size() < 4)
17758  {
17759  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17760  return("");
17761  }
17762  else
17763  {
17764  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17765  return("");
17766  }
17767  }
17768  }
17769  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
17770  {
17771  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
17772  {
17773  Utilities->CallLogPop(1570);
17774  if(ExitList.size() < 4)
17775  {
17776  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17777  return("");
17778  }
17779  else
17780  {
17781  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17782  return("");
17783  }
17784  }
17785  }
17786  Utilities->CallLogPop(1569);
17787  if(ExitList.size() < 4)
17788  {
17789  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
17790  return(" at " + StartName);
17791  }
17792  else
17793  {
17794  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
17795  return(" at " + StartName);
17796  }
17797 }
17798 
17799 // ---------------------------------------------------------------------------
17800 /* can't trust this as locations within a vector may not be contiguous
17801  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
17802  {
17803  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
17804  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
17805  //must be preceded by a TimeLoc departure
17806  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
17807  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
17808  {
17809  if((AVPtr + x) < TDEPtr->ActionVector.end())
17810  {
17811  AnsiString xx = (AVPtr + x)->Command;//test
17812  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
17813  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
17814  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
17815  {
17816  Utilities->CallLogPop();
17817  return false;
17818  }
17819  else if((AVPtr + x)->SequenceType == Finish)
17820  {
17821  Utilities->CallLogPop();
17822  return true;
17823  }
17824  }
17825  }
17826  Utilities->CallLogPop();
17827  return false;
17828  }
17829 */
17830 // ---------------------------------------------------------------------------
17831 
17832 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
17833 {
17834  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
17835  AnsiString FormatStr = "####0.0";
17836  AnsiString AvLateArrMins = "";
17837  AnsiString AvEarlyArrMins = "";
17838  AnsiString AvLatePassMins = "";
17839  AnsiString AvEarlyPassMins = "";
17840  AnsiString AvLateDepMins = "";
17841 
17842  if(LateArrivals > 0)
17843  {
17844  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
17845  }
17846  if(EarlyArrivals > 0)
17847  {
17848  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
17849  }
17850  if(LatePasses > 0)
17851  {
17852  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
17853  }
17854  if(EarlyPasses > 0)
17855  {
17856  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
17857  }
17858  if(LateDeps > 0)
17859  {
17860  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
17861  }
17862  PerfFile << '\n' << '\n' << "***************************************";
17863  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
17864 
17865  if(OnTimeArrivals != 1)
17866  {
17867  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
17868  }
17869  else
17870  {
17871  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
17872  }
17873  if(LateArrivals > 1)
17874  {
17875  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
17876  }
17877  else if(LateArrivals == 1)
17878  {
17879  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
17880  }
17881  else
17882  {
17883  PerfFile << LateArrivals << " late arrivals" << '\n';
17884  }
17885  if(EarlyArrivals > 1)
17886  {
17887  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
17888  }
17889  else if(EarlyArrivals == 1)
17890  {
17891  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
17892  }
17893  else
17894  {
17895  PerfFile << EarlyArrivals << " early arrivals" << '\n';
17896  }
17897  if(OnTimePasses != 1)
17898  {
17899  PerfFile << OnTimePasses << " on-time passes" << '\n';
17900  }
17901  else
17902  {
17903  PerfFile << OnTimePasses << " on-time pass" << '\n';
17904  }
17905  if(LatePasses > 1)
17906  {
17907  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
17908  }
17909  else if(LatePasses == 1)
17910  {
17911  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
17912  }
17913  else
17914  {
17915  PerfFile << LatePasses << " late passes" << '\n';
17916  }
17917  if(EarlyPasses > 1)
17918  {
17919  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
17920  }
17921  else if(EarlyPasses == 1)
17922  {
17923  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
17924  }
17925  else
17926  {
17927  PerfFile << EarlyPasses << " early passes" << '\n';
17928  }
17929  if(OnTimeDeps != 1)
17930  {
17931  PerfFile << OnTimeDeps << " on-time departures" << '\n';
17932  }
17933  else
17934  {
17935  PerfFile << OnTimeDeps << " on-time departure" << '\n';
17936  }
17937  if(LateDeps > 1)
17938  {
17939  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
17940  }
17941  else if(LateDeps == 1)
17942  {
17943  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
17944  }
17945  else
17946  {
17947  PerfFile << LateDeps << " late departures" << '\n';
17948  }
17949  TDateTime TempExcessLCDownTime;
17950  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
17951  {
17952 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
17953  //later perf summaries with lower values, changed at v2.8.0
17954 // {
17955  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
17956 // }
17957 /*
17958  else
17959  {
17960  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
17961  }
17962 */
17963  if(TempExcessLCDownTime > TDateTime(0))
17964  {
17965  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
17966  }
17967  }
17968 
17969  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
17970 
17971  if(ExcessLCDownMins > 0.1)
17972  {
17973  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
17974  }
17975  if(MissedStops != 1)
17976  {
17977  PerfFile << MissedStops << " missed stops" << '\n';
17978  }
17979  else
17980  {
17981  PerfFile << MissedStops << " missed stop" << '\n';
17982  }
17983  if(OtherMissedEvents != 1)
17984  {
17985  PerfFile << OtherMissedEvents << " other missed events" << '\n';
17986  }
17987  else
17988  {
17989  PerfFile << OtherMissedEvents << " other missed event" << '\n';
17990  }
17991  if(UnexpectedExits != 1)
17992  {
17993  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
17994  }
17995  else
17996  {
17997  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
17998  }
17999  if(IncorrectExits != 1)
18000  {
18001  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
18002  }
18003  else
18004  {
18005  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
18006  }
18007  if(NumFailures != 1)
18008  {
18009  PerfFile << NumFailures << " train failures" << '\n';
18010  }
18011  else
18012  {
18013  PerfFile << NumFailures << " train failure" << '\n';
18014  }
18015  if(AvHoursIntValue > 0)
18016  {
18017  if(AvHoursIntValue == 1)
18018  {
18019  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
18020  }
18021  else
18022  {
18023  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
18024  }
18025  }
18026  AnsiString AvLateMinsLocsNotReached = "";
18027 
18029  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
18030  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
18031 
18032  if(LocsNotReached > 0)
18033  {
18034  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
18035  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
18036  }
18037  if(SPADRisks != 1)
18038  {
18039  PerfFile << SPADRisks << " SPAD risks" << '\n';
18040  }
18041  else
18042  {
18043  PerfFile << SPADRisks << " SPAD risk" << '\n';
18044  }
18045  if(SPADEvents != 1)
18046  {
18047  PerfFile << SPADEvents << " SPADs" << '\n';
18048  }
18049  else
18050  {
18051  PerfFile << SPADEvents << " SPAD" << '\n';
18052  }
18053  if(Derailments != 1)
18054  {
18055  PerfFile << Derailments << " derailments" << '\n';
18056  }
18057  else
18058  {
18059  PerfFile << Derailments << " derailment" << '\n';
18060  }
18061  if(CrashedTrains != 1)
18062  {
18063  PerfFile << CrashedTrains << " crashed trains" << '\n';
18064  }
18065  else
18066  {
18067  PerfFile << CrashedTrains << " crashed train" << '\n';
18068  }
18069  PerfFile << '\n' << "***************************************" << '\n';
18070 
18071  bool DerailSPADFlag = false, CrashFlag = false;
18072 
18073  int OverallScorePercent = 100;
18074  int TotArrDep = 0;
18075  double TotLateMinsFactor = 1;
18076  double MissedStopAndSPADRiskFactor = 1;
18077  double NetNegFactor = 1;
18078 
18080  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
18081  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
18082  // 'no timetabled departures... message, which was inappropriate
18083 
18084  if((SPADEvents > 0) || (Derailments > 0))
18085  {
18086  OverallScorePercent = 5; // overrides other calculations
18087  DerailSPADFlag = true;
18088  }
18089  if(CrashedTrains > 0)
18090  {
18091  OverallScorePercent = 0; // overrides other calculations
18092  CrashFlag = true;
18093  }
18094  if(OverallScorePercent == 100)
18095  {
18096  if(TotArrDep > 0)
18097  {
18098  TotLateMinsFactor =
18100  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDep);
18101  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
18102  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
18103  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDep);
18104  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
18105  // of arrivals & departures, where 4% = half, 8% = a quarter etc
18106  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
18107  // NetNegfactor: product of the above two
18108  OverallScorePercent = 100 * NetNegFactor;
18109  }
18110  }
18111  if((TotArrDep > 0) || DerailSPADFlag || CrashFlag)
18112  // flag condits added at v1.1.4 - see above for what the error was
18113  {
18114  AnsiString OneFailureString = ", though the failure would account for some poor performance";
18115  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
18116  AnsiString AddedString = "";
18117  if(NumFailures == 1)
18118  {
18119  AddedString = OneFailureString;
18120  }
18121  if(NumFailures > 1)
18122  {
18123  AddedString = TwoOrMoreFailureString;
18124  }
18125  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
18126  AnsiString Rating = "";
18127  if(OverallScorePercent == 100)
18128  {
18129  Rating = "Perfect!";
18130  }
18131  else if(OverallScorePercent >= 95)
18132  {
18133  Rating = "Excellent";
18134  }
18135  else if(OverallScorePercent >= 90)
18136  {
18137  Rating = "Very good";
18138  }
18139  else if(OverallScorePercent >= 80)
18140  {
18141  Rating = "Good";
18142  }
18143  else if(OverallScorePercent >= 70)
18144  {
18145  Rating = "Fair";
18146  }
18147  else if(OverallScorePercent >= 60)
18148  {
18149  Rating = "Unacceptable" + AddedString;
18150  }
18151  else if(OverallScorePercent >= 50)
18152  {
18153  Rating = "Poor" + AddedString;
18154  }
18155  else if(OverallScorePercent >= 40)
18156  {
18157  Rating = "Bad" + AddedString;
18158  }
18159  else if(OverallScorePercent >= 30)
18160  {
18161  Rating = "Very bad" + AddedString;
18162  }
18163  else if(OverallScorePercent >= 20)
18164  {
18165  Rating = "Terrible" + AddedString;
18166  }
18167  else if(OverallScorePercent >= 10)
18168  {
18169  Rating = "Appalling" + AddedString;
18170  }
18171  else if(OverallScorePercent >= 5)
18172  {
18173  if(DerailSPADFlag)
18174  {
18175  Rating = "Disastrous - potential loss of life";
18176  }
18177  // SPADs/Derailments
18178  else
18179  {
18180  Rating = "Dire" + AddedString;
18181  }
18182  }
18183  else if(OverallScorePercent < 5)
18184  {
18185  if(CrashFlag)
18186  {
18187  Rating = "Catastrophic - loss of life"; // Crashes
18188  }
18189  else
18190  {
18191  Rating = "Abysmal";
18192  }
18193  }
18194  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
18195  }
18196  else
18197  {
18198  PerfFile << "\nThere were no timetabled departures or arrivals so there is insufficient information to provide a performance score or rating" << '\n';
18199  }
18200  PerfFile << '\n' << "***************************************";
18201  Utilities->CallLogPop(1736);
18202 }
18203 
18204 // ---------------------------------------------------------------------------
18205 
18207 {
18208  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
18209  for(unsigned int x = 0; x < TrainVector.size(); x++)
18210  {
18211  TTrain &Train = TrainVectorAt(58, x);
18212  if(Train.Crashed)
18213  // can't use background colours for crashed & derailed because same colour
18214  {
18215  CrashWarning = true;
18216  }
18217  else if(Train.Derailed)
18218  // can't use background colours for crashed & derailed because same colour
18219  {
18220  DerailWarning = true;
18221  }
18222  else if(Train.BackgroundColour == clSPADBackground)
18223  // use colour as that changes as soon as passes signal
18224  {
18225  SPADWarning = true;
18226  }
18227  else if(Train.BackgroundColour == clTrainFailedBackground)
18228  {
18229  TrainFailedWarning = true;
18230  }
18231  else if(Train.BackgroundColour == clCallOnBackground)
18232  // use colour as also stopped at signal
18233  {
18234  CallOnWarning = true;
18235  }
18236  else if(Train.BackgroundColour == clSignalStopBackground)
18237  // use colour to distinguish from call-on
18238  {
18239  SignalStopWarning = true;
18240  }
18241  else if(Train.BackgroundColour == clBufferAttentionNeeded)
18242  // use colour to distinguish from ordinary buffer stop
18243  {
18244  BufferAttentionWarning = true;
18245  }
18246  }
18247  Utilities->CallLogPop(1796);
18248 }
18249 
18250 // ---------------------------------------------------------------------------
18251 
18253 {
18254  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
18255 
18256  // calculate lateness for running trains
18259  for(unsigned int x = 0; x < TrainVector.size(); x++)
18260  {
18261  TTrain &Train = TrainVectorAt(64, x);
18262  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
18263  AVEntryPtr++)
18264  {
18265  if(AVEntryPtr < Train.ActionVectorEntryPtr)
18266  {
18267  continue;
18268  }
18269  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.StoppedAtLocation && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18270  TTClockTime))
18271  {
18272  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
18274  }
18275 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
18276  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
18277  TTClockTime))
18278  {
18279  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
18280  OperatingTrainArrDep++;
18281  }
18282 */
18283  }
18284  }
18285 
18286  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
18289 
18290  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
18291  {
18292  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
18293  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
18294  int IncrementalMinutes = 0;
18295  if(AVEntryLast.FormatType == Repeat)
18296  {
18297  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
18298  }
18299  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
18300  {
18301  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
18302  if(TTOD.RunningEntry != NotStarted)
18303  {
18304  continue;
18305  }
18306  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
18307  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
18308  bool TrainOperatingFlag = false;
18309  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
18310  {
18311  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
18312  {
18313  TrainOperatingFlag = true;
18314  break;
18315  }
18316  }
18317  if(TrainOperatingFlag)
18318  {
18319  continue;
18320  }
18321  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
18322  {
18323  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
18324  }
18325  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
18326  {
18327  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18328  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
18329  {
18330  break; // all the rest will also be greater (& default of -1 will be less)
18331  }
18332  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
18333  {
18334  break; // all the rest will also be greater (& default of -1 will be less)
18335  }
18336  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
18337  {
18338  break; // all the rest will also be greater (& default of -1 will be less)
18339  }
18340  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
18341  {
18342  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
18344  }
18345 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
18346  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
18347  {
18348  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
18349  NotStartedTrainArrDep++;
18350  }
18351 */
18352  }
18353  }
18354  }
18355  Utilities->CallLogPop(1894);
18356 }
18357 
18358 // ---------------------------------------------------------------------------
18359 
18361 // new v2.2.0 for OperatorActionPanel
18362 // clears entries then adds values for running trains then for continuation entries
18363 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
18364 {
18365  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
18366  OpTimeToActMultiMap.clear();
18367  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
18368 
18369  if(!TrainVector.empty())
18370  // build OpTimeToActMultiMap entries for running trains
18371  {
18372  AnsiString HeadCode;
18373  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
18374  int TrainID;
18375  THCandTrainPosParam HCandTrainPosParam;
18376  for(unsigned int x = 0; x < TrainVector.size(); x++)
18377  {
18378  HeadCode = TrainVectorAt(62, x).HeadCode;
18379  TrainID = TrainVectorAt(63, x).TrainID;
18380  HCandTrainPosParam.first = HeadCode;
18381  HCandTrainPosParam.second = TrainID;
18382  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
18383  if((TimeToAct >= 0) && (TimeToAct < 59.9))
18384  // -1 indicates don't display
18385  {
18386  OpTimeToActMultiMapEntry.first = TimeToAct;
18387  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18388  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18389  }
18390  }
18391  }
18392 /*
18393  * class TContinuationTrainExpectationEntry
18394  {
18395  public:
18396  AnsiString Description; ///< service description
18397  AnsiString HeadCode; ///< service headcode
18398  int RepeatNumber; ///< service RepeatNumber
18399  int IncrementalMinutes; ///< Repeat separation in minutes
18400  int IncrementalDigits; ///< Repeat headcode separation
18401  int VectorPosition; ///< TrackVectorPosition for the continuation element
18402  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
18403  };
18404 
18405  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
18406  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
18407  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
18408  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
18409 */
18410 
18412  // build OpTimeToActMultiMap entries for expected trains
18413  {
18414  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
18415  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
18416  float TimeToAct = 0; // minutes
18417  int DistanceToRedSignal = 0; // metres
18418  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
18419  // used to ensure only one train displayed for a given continuation
18420  ContinuationEntryVecPosVector.clear();
18421  bool LaterTrain = false;
18424  {
18425  LaterTrain = false;
18426  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
18427  {
18428  CTEIt++;
18429  continue; // not interested in running or exited trains
18430  }
18431  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
18432  {
18433  CTEIt++;
18434  continue;
18435  // don't include trains not entered yet when a train is already on the continuation
18436  }
18437  if(!ContinuationEntryVecPosVector.empty())
18438  {
18439  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
18440  {
18441  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
18442  {
18443  LaterTrain = true;
18444  ;
18445  // skip past remaining trains waiting to enter at same point
18446  break;
18447  }
18448  }
18449  }
18450  if(LaterTrain)
18451  {
18452  CTEIt++;
18453  continue;
18454  }
18455  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
18456  AnsiString HeadCode = CTEIt->second.HeadCode;
18457  float CurrentStopTime; // set to 0 at start of function
18458  float LaterStopTime; // set to 0 at start of function
18459  float RecoverableTime; // set to 0 at start of function
18460  int AvTrackSpeed; // set to 0 at start of function
18461  int TrainID = -1; // not yet allocated for train still to enter
18462  bool SigControlAndCanPassRedSignal = false;
18463  // doesn't apply for a continuation
18464  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
18465  // EntryPos always 0 for entering at a continuation
18466  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
18467  // at(1) to skip past the Start train value
18468  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
18469  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
18470  // for a train it's the one in front of LeadElement
18471  if(AvTrackSpeed < 30)
18472  {
18473  AvTrackSpeed = 30;
18474  }
18475  if(DistanceToRedSignal == -1)
18476  {
18477  TimeToAct = 60.0;
18478  }
18479  else
18480  {
18481  int Speed = AvTrackSpeed;
18482  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
18483  if(AvTrackSpeed > MaxSpeed)
18484  {
18485  Speed = MaxSpeed;
18486  }
18487  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
18488  // defined in timetable as under signaller control
18489  {
18490  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
18491  LaterStopTime = 0;
18492  }
18493  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
18494  // accel & decel taken into account in
18495  // CalcDistanceToRedSignalandStopTime
18496  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
18497  // don't need CurrentStopTime or RecoverableTime for continuation entries
18498  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
18499  TimeToAct += MinsBefEnter;
18500  }
18501  THCandTrainPosParam HCandTrainPosParam;
18502  HCandTrainPosParam.first = HeadCode;
18503  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
18504  // -1-CTE... because 2nd value covers TrainID if +ve &
18505  // continuation track vector position if -ve, -1 allows for vecpos being 0
18506  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
18507  {
18508  OpTimeToActMultiMapEntry.first = TimeToAct;
18509  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
18510  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
18511  }
18512  CTEIt++;
18513  }
18514  }
18515  Utilities->CallLogPop(2081);
18516 }
18517 
18518 // ---------------------------------------------------------------------------
18519 
18520 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
18521  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
18522  float &RecoverableTime, int &AvTrackSpeed)
18523 // new v2.2.0
18524 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
18525 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
18526 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
18527 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
18528 // aren't used - this means there is no display for the train in question
18529 {
18530  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
18531  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
18532  int DistanceToRedSignal = 0;
18533  int CumTrackSpeed = 0;
18534  // average track speed, in case need to use in time calc
18535  int TrackSpeedCount = 0;
18536 
18537  //below added at v2.6.1
18538  if(TrainID > -1)
18539  {
18540  TTrain &Train = TrainVectorAtIdent(51, TrainID);
18541  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
18542  Train.StationStopCalculated = false;
18543  }
18544  AvTrackSpeed = 0;
18545  int CurrentElement = TrackVectorPosition;
18546  int CurrentEntryPos = TrackVectorPositionEntryPos;
18547  int NextElement;
18548  int NextEntryPos;
18549  int NextExitPos;
18550 
18551  CurrentStopTime = 0;
18552  LaterStopTime = 0;
18553  RecoverableTime = 0;
18554  if(CurrentElement == -1) // end element, no action needed
18555  {
18556  Utilities->CallLogPop(2094);
18557  return(-1);
18558  }
18559  int CurrentExitPos;
18560 
18561  // get ExitPos for first element to be measured
18562  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
18563  {
18564  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
18565  {
18566  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
18567  {
18568  CurrentExitPos = 1;
18569  }
18570  else
18571  {
18572  CurrentExitPos = 3;
18573  }
18574  }
18575  else
18576  {
18577  CurrentExitPos = 0; // trailing point
18578  }
18579  }
18580  else
18581  {
18582  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
18583  }
18584  // get CumTrackSpeed for first measured element
18585 
18586  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
18587  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
18588 
18589  // check if currently stopped at a location, and if so add the remaining dwell time
18590  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
18591  if(TrainID > -1)
18592  // -1 for a continuation and can't be at a location as not yet entered
18593  {
18594  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
18596  // this used to deduct from RecoverableTime when arrive at a location
18597  if(Train.StoppedAtLocation)
18598  {
18599  if(Train.StoppedForTrainInFront)
18600  {
18601  Utilities->CallLogPop(2082);
18602  return(-1); // no action needed
18603  }
18604  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
18605  {
18606  Utilities->CallLogPop(2083);
18607  return(-1); // not due a departure so no action needed
18608  }
18609  else // due a departure
18610  {
18611  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
18612  // can't convert a TDateTime to a float directly
18613  CurrentStopTime = float(TimeToDepart);
18614  AVPtr++;
18615  }
18616  }
18617  }
18618  // check if CurrentElement is a red signal, but ok if autosig route after
18619  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18620  // ok if autosig route after red signal
18621  {
18622  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
18623  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
18624  int RouteNumber; // holder for referenced value, not used
18625  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18626  {
18627  Utilities->CallLogPop(2078);
18628  return(-1);
18629  }
18630  else if(SigControlAndCanPassRedSignal)
18631  // ignore signal and increment CurrentElement to NextElement
18632  {
18633  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
18634  {
18635  if((NextEntryPos == 0) || (NextEntryPos == 2))
18636  // leading entry point
18637  {
18638  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
18639  {
18640  NextExitPos = 1;
18641  }
18642  else
18643  {
18644  NextExitPos = 3;
18645  }
18646  }
18647  else
18648  {
18649  NextExitPos = 0; // trailing entry point
18650  }
18651  }
18652  else
18653  {
18654  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18655  }
18656  CurrentElement = NextElement;
18657  CurrentEntryPos = NextEntryPos;
18658  CurrentExitPos = NextExitPos;
18659  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
18660  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
18661  }
18662  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
18663  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
18664  {
18665  Utilities->CallLogPop(2084);
18666  return(0);
18667  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
18668  }
18669  }
18670  int LaterStopNumber = 0;
18671  int x = 0;
18672  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
18673 
18674  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
18675  // not red signal next (in fwd direction) so enter loop to calc CumLength
18676  {
18677  x++; // added in v2.4.0 as above
18678  if(x > 5000)
18679  {
18680  Utilities->CallLogPop(2120);
18681  return(-1);
18682  }
18683  if(CurrentEntryPos > 1)
18684  {
18685  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
18686  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
18687  }
18688  else
18689  {
18690  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
18691  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
18692  }
18693  TrackSpeedCount++;
18694 
18695  // added at v2.6.1 to find DistanceToStationStop for trains running early
18696  if(TrainID > -1) //can ignore continuation entries as these don't run early
18697  {
18698  TTrain &Train = TrainVectorAtIdent(52, TrainID);
18699  if(!Train.StationStopCalculated)
18700  {
18701  if(Train.TrainMode == Timetable)
18702  {
18703  bool StopRequired = false;
18704  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
18705  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
18706  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
18707  {
18708  // no need to add in the length of element to CumulativeLength
18709  if(StopRequired)
18710  {
18711  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
18712  Train.StationStopCalculated = true; //don't want to update it with later stops
18713  }
18714  }
18715  }
18716  }
18717  }
18718  // check for train in front, but if on a bridge on other track then ok
18719  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
18720  int TrainOnElement;
18721  if(TE.TrackType != Bridge)
18722  {
18723  TrainOnElement = TE.TrainIDOnElement;
18724  }
18725  else
18726  {
18727  if(CurrentEntryPos > 1)
18728  {
18729  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
18730  }
18731  else
18732  {
18733  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
18734  }
18735  }
18736  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
18737  // train in front before red signal
18738  {
18739  Utilities->CallLogPop(2085);
18740  return(-1);
18741  }
18742  // add to stoptime if required
18743  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
18744  {
18745  double StopTimeDouble;
18746  while(AVPtr->FormatType == PassTime)
18747  {
18748  AVPtr++; // skip past any passes
18749  }
18750  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
18751  (AVPtr->FormatType == TimeTimeLoc)))
18752  // stop due here so calc dwell time & advance Ptr
18753  {
18754  if(AVPtr->FormatType == TimeTimeLoc)
18755  {
18756  LaterStopNumber++;
18757  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18758  if(StopTimeDouble < 0.5)
18759  {
18760  StopTimeDouble = 0.5;
18761  }
18762  // at least 30 secs delay at station
18763  // can't convert a TDateTime to a float directly
18764  LaterStopTime += float(StopTimeDouble);
18765  RecoverableTime += StopTimeDouble - 0.5;
18766  if((LaterStopNumber == 1) && (TrainID > -1))
18767  {
18768  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18769  }
18770  AVPtr++;
18771  }
18772  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
18773  {
18774  if((AVPtr + 1)->FormatType == TimeLoc)
18775  // must be a departure
18776  {
18777  LaterStopNumber++;
18778  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
18779  // can't convert a TDateTime to a float directly
18780  if(StopTimeDouble < 0.5)
18781  {
18782  StopTimeDouble = 0.5;
18783  }
18784  // at least 30 secs delay at station
18785  LaterStopTime += float(StopTimeDouble);
18786  RecoverableTime += StopTimeDouble - 0.5;
18787  if((LaterStopNumber == 1) && (TrainID > -1))
18788  {
18789  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
18790  }
18791  AVPtr++;
18792  AVPtr++;
18793  }
18794  else // not a departure, does something else at the location so no calculation needed
18795  {
18796  Utilities->CallLogPop(2086);
18797  return(-1);
18798  }
18799  }
18800  }
18801  }
18802  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
18803  if(NextElement == -1) // reached end element, no action needed
18804  {
18805  Utilities->CallLogPop(2077);
18806  return(-1);
18807  }
18808  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
18809  // get NextExitPos
18810  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
18811  {
18812  if((NextEntryPos == 0) || (NextEntryPos == 2))
18813  // leading entry point
18814  {
18815  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
18816  {
18817  NextExitPos = 1;
18818  }
18819  else
18820  {
18821  NextExitPos = 3;
18822  }
18823  }
18824  else
18825  {
18826  NextExitPos = 0; // trailing entry point
18827  }
18828  }
18829  else
18830  {
18831  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
18832  }
18833  CurrentElement = NextElement;
18834  CurrentEntryPos = NextEntryPos;
18835  CurrentExitPos = NextExitPos;
18836  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
18837  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
18838  }
18839  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
18840  // ok if autosig route after red signal, no action needed
18841  {
18842  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
18843  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
18844  int RouteNumber; // holder for referenced value, not used
18845  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
18846  {
18847  Utilities->CallLogPop(2095);
18848  return(-1);
18849  }
18850  }
18851  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
18852  // average line speed/2 (for half distance accelerating and half decelerating.
18853 
18854  float MaxAllowableSpeed;
18855 
18856  if(TrackSpeedCount > 0)
18857  {
18858  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
18859  }
18860  else // shouldn't reach here but include to prevent divide by zero error
18861  {
18862  if(CurrentEntryPos > 1)
18863  {
18864  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
18865  }
18866  else
18867  {
18868  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
18869  }
18870  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
18871  }
18872  float KmPerLocationStop;
18873 
18874  if(LaterStopNumber > 0)
18875  {
18876  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
18877  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
18878  }
18879  else
18880  {
18881  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
18882  // using linear trendline for accel & decel distance at various speeds
18883  // at half braking, speed never < 60 using this
18884  }
18885  if(AvTrackSpeed > MaxAllowableSpeed)
18886  {
18887  AvTrackSpeed = MaxAllowableSpeed;
18888  }
18889  Utilities->CallLogPop(2096);
18890  return(DistanceToRedSignal);
18891 }
18892 
18893 // ---------------------------------------------------------------------------
18894 // end of TTrainController entries
18895 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8320
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17455
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:113
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:347
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:50
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14072
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:314
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:313
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:443
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:646
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1665
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:885
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:151
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14095
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:13758
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:335
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:18360
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:754
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15110
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6950
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:94
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:51
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:981
Create
@ Create
Definition: TrainUnit.h:50
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:733
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18461
Arrive
@ Arrive
Definition: TrainUnit.h:50
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:50
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:312
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:353
Depart
@ Depart
Definition: TrainUnit.h:50
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:702
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:10981
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:700
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:393
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:757
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:307
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7061
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1582
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:51
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:891
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1594
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:693
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:119
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:741
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1606
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:68
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:64
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:460
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:694
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6108
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:65
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:397
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:69
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19209
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13042
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:335
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:967
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:379
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:950
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:740
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:308
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:946
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7196
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:355
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:434
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:335
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6222
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:740
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9381
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:331
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:882
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:668
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14841
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:793
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:973
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:619
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:980
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18251
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:740
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10934
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1586
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8292
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:113
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:933
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:690
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14856
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:242
Intermediate
@ Intermediate
Definition: TrainUnit.h:75
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:931
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6037
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:947
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3438
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:628
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:64
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:456
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:418
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9343
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:52
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:449
NotStarted
@ NotStarted
Definition: TrainUnit.h:86
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10625
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:787
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:736
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:315
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:705
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:343
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:184
LeadMid
@ LeadMid
Definition: TrainUnit.h:275
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:780
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18724
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13838
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:945
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6243
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:686
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:987
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:229
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:983
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:192
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:797
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:760
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:676
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8598
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:14959
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:275
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:833
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:15818
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14897
StartNew
@ StartNew
Definition: TrainUnit.h:64
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:50
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:275
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:761
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:316
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:51
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:408
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:695
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:17999
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:10542
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:956
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5673
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:463
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:163
TTrain
Definition: TrainUnit.h:279
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:953
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:10561
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:155
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11255
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:828
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:227
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:810
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:440
GapJump
@ GapJump
Definition: TrackUnit.h:67
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:974
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:782
NoSequence
@ NoSequence
Definition: TrainUnit.h:75
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:375
Finish
@ Finish
Definition: TrainUnit.h:75
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:5988
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:8979
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:335
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:141
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1633
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:335
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:329
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:202
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:649
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:333
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14938
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:777
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:298
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:731
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:422
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:95
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:664
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:14661
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1349
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5905
End
@ End
Definition: TrackUnit.h:77
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:365
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6824
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8601
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:310
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackElement.at(At)
Definition: TrackUnit.cpp:10354
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:65
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:319
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:371
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15021
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:127
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:941
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:581
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:991
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:786
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:186
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:387
SignalPost
@ SignalPost
Definition: TrackUnit.h:67
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:935
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:426
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1610
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:318
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:963
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:186
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5317
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:11605
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:127
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:788
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:717
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:837
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:385
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:76
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:436
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:66
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:785
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:754
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:879
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1859
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:440
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:783
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:951
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:64
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:96
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:113
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:440
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:10614
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:929
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:416
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:954
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9879
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:688
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:335
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:414
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:79
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:979
Pass
@ Pass
Definition: TrainUnit.h:52
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:740
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:702
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:70
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2962
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11073
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:770
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:966
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:447
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:720
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:818
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7433
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:939
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:662
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:323
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:292
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:972
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:766
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8304
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:975
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:188
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:64
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:198
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6680
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:757
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17175
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:428
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14340
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10491
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:600
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:286
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3170
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:986
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:424
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4928
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2483
RearSplit
@ RearSplit
Definition: TrainUnit.h:50
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:111
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:52
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:778
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:612
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:936
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:764
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:959
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:137
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10389
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2894
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6652
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14438
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:52
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:844
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13142
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
new v2.2.0, incremented in Interface.cpp, controls updating for OpTimeToActPanel
Definition: TrainUnit.h:802
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:349
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3388
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:932
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:300
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8114
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7393
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2886
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:769
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:443
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:420
NewService
@ NewService
Definition: TrainUnit.h:50
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15174
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1449
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:440
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:351
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1593
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:341
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:200
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:196
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:775
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:976
Crossover
@ Crossover
Definition: TrackUnit.h:67
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4538
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:682
AtLocation
@ AtLocation
Definition: TrainUnit.h:70
Signal
@ Signal
Definition: TrackUnit.h:77
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:938
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:969
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:750
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:155
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17692
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:816
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:17364
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1594
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:422
Exited
@ Exited
Definition: TrainUnit.h:86
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackElement.at(At)
Definition: TrackUnit.cpp:10340
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14872
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7680
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17518
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:988
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2100
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:440
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:57
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1615
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:928
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:440
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:700
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:288
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:742
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:50
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:395
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:754
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:958
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:934
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:335
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:64
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:970
Leave
@ Leave
Definition: TrainUnit.h:50
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:790
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:712
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:738
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:412
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:363
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2867
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:937
NoFormat
@ NoFormat
Definition: TrainUnit.h:64
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:750
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:64
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2412
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:440
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3417
Enter
@ Enter
Definition: TrainUnit.h:50
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7648
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:944
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6182
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14921
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:406
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:253
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:13672
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:692
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:8633
Timetable
@ Timetable
Definition: TrainUnit.h:58
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14026
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6735
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:748
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:10924
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:741
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:109
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17361
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:740
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:295
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:18252
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:80
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:401
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:710
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:240
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:746
Terminate
@ Terminate
Definition: TrainUnit.h:50
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:754
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:753
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:440
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:454
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:338
Running
@ Running
Definition: TrainUnit.h:86
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:262
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3514
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:880
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13014
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14450
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:18520
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15197
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:414
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11144
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:691
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:791
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:389
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2297
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18376
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:806
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1580
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9065
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:8680
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:9572
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:961
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:130
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:8691
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:978
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1667
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:754
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3366
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3066
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:422
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:761
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11022
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:70
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:52
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1578
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8261
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2930
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:101
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15043
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:243
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:955
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14417
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:808
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:441
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4494
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:255
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:17724
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9397
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:51
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:325
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:373
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11105
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:99
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:8705
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:887
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:404
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:731
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1648
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:190
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:132
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:960
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:204
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:762
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:860
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:534
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4951
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:115
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:424
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1584
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1594
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:117
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:304
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:984
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:655
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6482
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:653
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9049
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:878
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:776
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:321
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15210
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:728
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:949
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Points
@ Points
Definition: TrackUnit.h:67
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:724
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:361
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:771
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:13571
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6523
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:359
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:754
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:789
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:52
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3362
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:968
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:367
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:458
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16636
Continuation
@ Continuation
Definition: TrackUnit.h:67
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:740
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7406
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:726
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:784
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:432
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9751
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:331
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:17832
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3024
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:883
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:52
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:723
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:886
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:982
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9428
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:89
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:438
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:622
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:705
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6425
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:445
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:289
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:465
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:52
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:965
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19039
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:141
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:854
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:957
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10782
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:290
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:261
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:685
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:664
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8224
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:63
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:740
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:295
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:709
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:164
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:80
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:707
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:952
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18149
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18105
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:744
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:103
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:259
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:75
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:890
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:302
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:49
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:664
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:335
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:779
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:989
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:80
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9019
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14364
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:6999
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17375
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:381
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5369
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:17122
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9034
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:125
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13021
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:311
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8368
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15003
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:162
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:70
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:900
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:357
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:194
NoMode
@ NoMode
Definition: TrainUnit.h:58
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:781
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11453
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:102
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:713
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:754
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:64
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:153
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:214
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:799
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:774
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:752
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5035
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:440
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9416
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:948
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:274
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:725
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:217
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14060
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7145
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:410
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:651
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1588
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:121
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:327
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:715
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:942
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:985
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:711
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2988
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:103
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:123
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:377
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:977
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:103
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4661
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:684
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6585
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:369
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:795
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:754
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:940
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6169
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:709
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:64
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:51
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:714
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:18206
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:680
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:64
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:768
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:971
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:812
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:666
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:105
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4940
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:399
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:670
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:306
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:716
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:713
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:65
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:778
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2912
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:91
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:626
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1038
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:962
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:17406
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6453
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3408
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:107
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2500
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:186
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6766
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:309
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:77
Repeat
@ Repeat
Definition: TrainUnit.h:65
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:930
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13030
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:653
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:754
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:160
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:68
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:441
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:964
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:710
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:823
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:225
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5679
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:17148
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:58
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:451
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:888
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:160
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:876
Bridge
@ Bridge
Definition: TrackUnit.h:67
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:943
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:257
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:335
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:74
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:391
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:75
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:67
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:111
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:103